Last active
December 7, 2015 16:39
-
-
Save UjwalManjunath/632a3880c9faeab69226 to your computer and use it in GitHub Desktop.
IOS Reachability: Swift 2.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import SystemConfiguration | |
public let ReachabilityChangedNotification = "ReachabilityChangedNotification" | |
class HTSDReachabiltiy: NSObject { | |
typealias NetworkReachable = (HTSDReachabiltiy) -> () | |
typealias NetworkUnreachable = (HTSDReachabiltiy) -> () | |
enum NetworkStatus: CustomStringConvertible { | |
case NotReachable, ReachableViaWiFi, ReachableViaWWAN | |
internal var description: String { | |
switch self { | |
case .ReachableViaWWAN: | |
return "Cellular" | |
case .ReachableViaWiFi: | |
return "WiFi" | |
case .NotReachable: | |
return "No Connection" | |
} | |
} | |
} | |
// MARK: - *** Public properties *** | |
var whenReachable: NetworkReachable? | |
var whenUnreachable: NetworkUnreachable? | |
var reachableOnWWAN: Bool | |
var notificationCenter = NSNotificationCenter.defaultCenter() | |
var currentReachabilityStatus: NetworkStatus { | |
if isReachable() { | |
if isReachableViaWiFi() { | |
return .ReachableViaWiFi | |
} | |
if isRunningOnDevice { | |
return .ReachableViaWWAN | |
} | |
} | |
return .NotReachable | |
} | |
var currentReachabilityString: String { | |
return "\(currentReachabilityStatus)" | |
} | |
// MARK: - *** Initialisation methods *** | |
required init(reachabilityRef: SCNetworkReachability) { | |
reachableOnWWAN = true | |
self.reachabilityRef = reachabilityRef | |
} | |
convenience init(hostname: String) { | |
let ref = SCNetworkReachabilityCreateWithName(nil, (hostname as NSString).UTF8String) | |
self.init(reachabilityRef: ref!) | |
} | |
class func reachabilityForInternetConnection() -> HTSDReachabiltiy { | |
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)) | |
} | |
return HTSDReachabiltiy(reachabilityRef: ref!) | |
} | |
class func reachabilityForLocalWiFi() -> HTSDReachabiltiy { | |
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)) | |
} | |
return HTSDReachabiltiy(reachabilityRef: ref!) | |
} | |
// MARK: - *** Notifier methods *** | |
func startNotifier() -> Bool { | |
reachabilityObject = self | |
_ = 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), 100 * 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() { | |
reachabilityObject = nil | |
if let timer = dispatch_timer { | |
dispatch_source_cancel(timer) | |
dispatch_timer = nil | |
} | |
} | |
// MARK: - *** Connection test methods *** | |
func isReachable() -> Bool { | |
return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in | |
return self.isReachableWithFlags(flags) | |
}) | |
} | |
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 | |
} | |
} | |
return false | |
} | |
} | |
return false | |
} | |
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) { | |
return false | |
} | |
} | |
return true | |
} | |
return false | |
} | |
} | |
// MARK: - *** Private methods *** | |
private var isRunningOnDevice: Bool = { | |
#if (arch(i386) || arch(x86_64)) && os(iOS) | |
return false | |
#else | |
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("com.how-to-speak-doctor.reachability_timer_queue", nil) | |
}() | |
private var previousReachabilityFlags: SCNetworkReachabilityFlags? | |
func timerFired() { | |
let currentReachabilityFlags = reachabilityFlags | |
if let reviousReachabilityFlags = previousReachabilityFlags { | |
if currentReachabilityFlags != reviousReachabilityFlags { | |
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 { | |
block(self) | |
} | |
} else { | |
if let block = whenUnreachable { | |
block(self) | |
} | |
} | |
notificationCenter.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 = SCNetworkReachabilityFlags() | |
let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef!, &flags) | |
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 { | |
#if os(iOS) | |
return flags.rawValue & SCNetworkReachabilityFlags.IsWWAN.rawValue != 0 | |
#else | |
return false | |
#endif | |
} | |
private func isReachable(flags: SCNetworkReachabilityFlags) -> Bool { | |
return flags.rawValue & SCNetworkReachabilityFlags.Reachable.rawValue != 0 | |
} | |
private func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool { | |
return flags.rawValue & SCNetworkReachabilityFlags.ConnectionRequired.rawValue != 0 | |
} | |
private func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool { | |
return flags.rawValue & SCNetworkReachabilityFlags.InterventionRequired.rawValue != 0 | |
} | |
private func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool { | |
return flags.rawValue & SCNetworkReachabilityFlags.ConnectionOnTraffic.rawValue != 0 | |
} | |
private func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool { | |
return flags.rawValue & SCNetworkReachabilityFlags.ConnectionOnDemand.rawValue != 0 | |
} | |
func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool { | |
return flags.rawValue & SCNetworkReachabilityFlags.ConnectionOnTraffic.rawValue | SCNetworkReachabilityFlags.ConnectionOnDemand.rawValue != 0 | |
} | |
private func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool { | |
return flags.rawValue & SCNetworkReachabilityFlags.TransientConnection.rawValue != 0 | |
} | |
private func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool { | |
return flags.rawValue & SCNetworkReachabilityFlags.IsLocalAddress.rawValue != 0 | |
} | |
private func isDirect(flags: SCNetworkReachabilityFlags) -> Bool { | |
return flags.rawValue & SCNetworkReachabilityFlags.IsDirect.rawValue != 0 | |
} | |
private func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool { | |
let testcase = SCNetworkReachabilityFlags.ConnectionRequired.rawValue | SCNetworkReachabilityFlags.TransientConnection.rawValue | |
return flags.rawValue & testcase == testcase | |
} | |
private var reachabilityFlags: SCNetworkReachabilityFlags { | |
var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags.TransientConnection | |
let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef!, &flags) | |
if gotFlags { | |
return flags | |
} | |
return SCNetworkReachabilityFlags.TransientConnection | |
} | |
override var description: String { | |
var W: String | |
if isRunningOnDevice { | |
W = isOnWWAN(reachabilityFlags) ? "W" : "-" | |
} else { | |
W = "X" | |
} | |
let R = isReachable(reachabilityFlags) ? "R" : "-" | |
let c = isConnectionRequired(reachabilityFlags) ? "c" : "-" | |
let t = isTransientConnection(reachabilityFlags) ? "t" : "-" | |
let i = isInterventionRequired(reachabilityFlags) ? "i" : "-" | |
let C = isConnectionOnTraffic(reachabilityFlags) ? "C" : "-" | |
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 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment