| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- /*
- Copyright (c) 2014, Ashley Mills
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- */
- import SystemConfiguration
- import Foundation
- let ReachabilityChangedNotification = "ReachabilityChangedNotification"
- class Reachability: NSObject, Printable {
-
- typealias NetworkReachable = (Reachability) -> ()
- typealias NetworkUneachable = (Reachability) -> ()
- enum NetworkStatus: Printable {
-
- case NotReachable, ReachableViaWiFi, ReachableViaWWAN
-
- 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: NetworkUneachable?
- var reachableOnWWAN: Bool
- var currentReachabilityStatus: NetworkStatus {
- if isReachable() {
- if isReachableViaWiFi() {
- return .ReachableViaWiFi
- }
- if isRunningOnDevice {
- return .ReachableViaWWAN;
- }
- }
-
- return .NotReachable
- }
-
- var currentReachabilityString: String {
- return "\(currentReachabilityStatus)"
- }
-
- // MARK: - *** Initialisation methods ***
- convenience init(hostname: String) {
- let ref = SCNetworkReachabilityCreateWithName(nil, (hostname as NSString).UTF8String).takeRetainedValue()
- self.init(reachabilityRef: ref)
- }
-
- 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 {
-
- 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
- localWifiAddress.sin_addr.s_addr = in_addr_t(Int64(0xA9FE0000).bigEndian)
-
- let ref = withUnsafePointer(&localWifiAddress) {
- SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, UnsafePointer($0)).takeRetainedValue()
- }
- return Reachability(reachabilityRef: ref)
- }
-
- // MARK: - *** Notifier methods ***
- func startNotifier() -> Bool {
-
- reachabilityObject = self
- let reachability = self.reachabilityRef!
-
- previousReachabilityFlags = reachabilityFlags;
- timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "timerFired:", userInfo: nil, repeats: true)
-
- return true;
- }
-
- func stopNotifier() {
-
- reachabilityObject = nil;
-
- timer?.invalidate()
- 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 timer: NSTimer?
- private var previousReachabilityFlags: SCNetworkReachabilityFlags?
-
- private init(reachabilityRef: SCNetworkReachability) {
- reachableOnWWAN = true;
- self.reachabilityRef = reachabilityRef;
- }
-
- func timerFired(timer: NSTimer) {
-
- let currentReachabilityFlags = reachabilityFlags
- if let _previousReachabilityFlags = previousReachabilityFlags {
- if currentReachabilityFlags != previousReachabilityFlags {
- reachabilityChanged(currentReachabilityFlags)
- previousReachabilityFlags = currentReachabilityFlags
- }
- }
- }
-
- private func reachabilityChanged(flags: SCNetworkReachabilityFlags) {
- if isReachableWithFlags(flags) {
- if let block = whenReachable {
- block(self)
- }
- } else {
- if let block = whenUnreachable {
- 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
- }
- private func isReachable(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsReachable) != 0
- }
- private func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionRequired) != 0
- }
- private func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsInterventionRequired) != 0
- }
- private func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0
- }
- private func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnDemand) != 0
- }
- func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand) != 0
- }
- private func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsTransientConnection) != 0
- }
- private func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsLocalAddress) != 0
- }
- private func isDirect(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsDirect) != 0
- }
- private func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool {
- 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 {
-
- 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
- }
- }
|