| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- /*
- 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.
- */
- let ReachabilityChangedNotification = "ReachabilityChangedNotification"
- class Reachability {
-
- typealias NetworkReachable = (Reachability) -> ()
- typealias NetworkUneachable = (Reachability) -> ()
- typealias StartNotifier = (Reachability) -> (Bool)
-
- enum NetworkStatus {
- // Apple NetworkStatus Compatible Names.
- case NotReachable, ReachableViaWiFi, ReachableViaWWAN
- }
-
- var reachabilityRef: SCNetworkReachability?
- var reachabilitySerialQueue: dispatch_queue_t?
- var reachabilityObject: AnyObject?
- var reachableBlock: NetworkReachable?
- var unreachableBlock: NetworkUneachable?
- var startNotifierBlock: StartNotifier?
- var reachableOnWWAN: Bool
-
- init(reachabilityRef: SCNetworkReachability) {
- reachableOnWWAN = true;
- self.reachabilityRef = reachabilityRef;
- }
-
- 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)
- }
-
- func startNotifier() -> Bool {
-
- reachabilityObject = self
- // First, we need to create a serial queue.
- // We allocate this once for the lifetime of the notifier.
- reachabilitySerialQueue = dispatch_queue_create("com.joylordsystems.reachability", nil)
- if reachabilitySerialQueue == nil {
- return false
- }
-
- // TODO:
- let callback:(SCNetworkReachability!, SCNetworkReachabilityFlags, UnsafeMutablePointer<Void>) -> () = { (target: SCNetworkReachability!, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) in
- self.reachabilityChanged(flags)
- }
-
- let p = UnsafeMutablePointer<(SCNetworkReachability!, SCNetworkReachabilityFlags, UnsafeMutablePointer<Void>) -> Void>.alloc(1)
- p.initialize(callback)
-
- let cp = COpaquePointer(p) // convert UnsafeMutablePointer to COpaquePointer
- let fp = CFunctionPointer<(SCNetworkReachability!, SCNetworkReachabilityFlags, UnsafeMutablePointer<Void>) -> Void>(cp) // convert COpaquePointer to CFunctionPointer
-
- if SCNetworkReachabilitySetCallback(self.reachabilityRef, fp, nil) != 0 {
-
- println("SCNetworkReachabilitySetCallback() failed: \(SCErrorString(SCError()))")
-
- // Clear out the dispatch queue
- reachabilitySerialQueue = nil;
- reachabilityObject = nil;
-
- return false;
- }
-
- // Set it as our reachability queue, which will retain the queue
- if SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) == 0
- {
- println("SCNetworkReachabilitySetDispatchQueue() failed: \(SCErrorString(SCError()))")
-
- // First stop, any callbacks!
- SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
-
- // Then clear out the dispatch queue.
- reachabilitySerialQueue = nil
- reachabilityObject = nil
-
- return false
- }
-
- return true;
- }
-
- func stopNotifier() {
-
- // First stop, any callbacks!
- SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
-
- // Unregister target from the GCD serial dispatch queue.
- SCNetworkReachabilitySetDispatchQueue(nil, nil);
-
- reachabilitySerialQueue = nil;
- reachabilityObject = nil;
- }
-
- func reachabilityChanged(flags: SCNetworkReachabilityFlags) {
- if isReachableWithFlags(flags) {
- if let block = reachableBlock {
- block(self)
- }
- } else {
- if let block = unreachableBlock {
- block(self)
- }
- }
-
- // this makes sure the change notification happens on the MAIN THREAD
- dispatch_async(dispatch_get_main_queue()) {
- NSNotificationCenter.defaultCenter().postNotificationName(ReachabilityChangedNotification, object:self)
- }
- }
-
- func isReachableWithFlags(flags: SCNetworkReachabilityFlags) -> Bool {
-
- let reachable = isReachable(flags)
-
- if reachable == 0 {
- return false
- }
-
- if isConnectionRequiredOrTransient(flags) {
- return false
- }
-
- #if TARGET_OS_IPHONE
- if isOnWWAN(flags) && !reachableOnWWAN {
- // We don't want to connect when on 3G.
- return false
- }
- #endif
-
- return true
- }
-
- func isReachableWithTest(test: (SCNetworkReachabilityFlags) -> (Bool)) -> Bool {
- var flags: SCNetworkReachabilityFlags = 0
- let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, &flags) != 0
- if gotFlags {
- return test(flags)
- }
-
- return false
- }
-
- func isReachable() -> Bool {
- return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
- return self.isReachableWithFlags(flags)
- })
- }
-
- func isReachableViaWWAN() -> Bool {
- #if TARGET_OS_IPHONE
- return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
- // Check we're REACHABLE
- if self.isReachable(flags) {
-
- // Now, check we're on WWAN
- if self.isOnWWAN(flags) {
- return true
- }
- }
- return false
- })
- #endif
- return false
- }
-
- func isReachableViaWiFi() -> Bool {
-
- return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
-
- // Check we're reachable
- if self.isReachable(flags) {
- #if TARGET_OS_IPHONE
-
- // Check we're NOT on WWAN
- if self.isOnWWAN(flags) {
- return false
- }
- #endif
- return true
- }
-
- return false
- })
- }
-
- // WWAN may be available, but not active until a connection has been established.
- // WiFi may require a connection for VPN on Demand.
- func isConnectionRequired() -> Bool {
- return connectionRequired()
- }
-
- func connectionRequired() -> Bool {
- return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
- return self.isConnectionRequired(flags)
- })
- }
-
- // Dynamic, on demand connection?
- func isConnectionOnDemand() -> Bool {
- return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
- return self.isConnectionRequired(flags) && self.isConnectionOnTrafficOrDemand(flags)
- })
- }
-
- // Is user intervention required?
- func isInterventionRequired() -> Bool {
- return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
- return self.isConnectionRequired(flags) && self.isInterventionRequired(flags)
- })
- }
-
- func isOnWWAN(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsWWAN) != 0
- }
- func isReachable(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsReachable) != 0
- }
- func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionRequired) != 0
- }
- func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsInterventionRequired) != 0
- }
- func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0
- }
- func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnDemand) != 0
- }
- func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand) != 0
- }
- func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsTransientConnection) != 0
- }
- func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsLocalAddress) != 0
- }
- func isDirect(flags: SCNetworkReachabilityFlags) -> Bool {
- return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsDirect) != 0
- }
- func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool {
- let testcase = SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
- return flags & testcase == testcase
- }
-
- // MARK: - *** xx methods ***
-
- var currentReachabilityStatus: NetworkStatus {
- if isReachable() {
- if isReachableViaWiFi() {
- return .ReachableViaWiFi
- }
- #if TARGET_OS_IPHONE
- return .ReachableViaWWAN;a
- #endif
- }
-
- return .NotReachable
- }
-
- var reachabilityFlags: SCNetworkReachabilityFlags {
- var flags: SCNetworkReachabilityFlags = 0
- let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, &flags) != 0
- if gotFlags {
- return flags
- }
-
- return 0
- }
-
- var currentReachabilityString: String {
-
- switch currentReachabilityStatus {
- case .ReachableViaWWAN:
- return NSLocalizedString("Cellular", comment: "")
- case .ReachableViaWiFi:
- return NSLocalizedString("WiFi", comment: "")
- case .NotReachable:
- return NSLocalizedString("No Connection", comment: "");
- }
- }
-
- var currentReachabilityFlags: String {
- #if TARGET_OS_IPHONE
- let W = isOnWWAN(reachabilityFlags) ? "W" : "-"
- #else
- let W = "X"
- #endif
- 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
- reachableBlock = nil
- unreachableBlock = nil
- }
- }
|