Skip to content

Instantly share code, notes, and snippets.

@UjwalManjunath
Last active December 7, 2015 16:39
Show Gist options
  • Save UjwalManjunath/632a3880c9faeab69226 to your computer and use it in GitHub Desktop.
Save UjwalManjunath/632a3880c9faeab69226 to your computer and use it in GitHub Desktop.
IOS Reachability: Swift 2.0
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