Browse Source

Replace timer with SCNetworkReachabilityCallback

Ashley Mills 10 years ago
parent
commit
daf6a4ed67

+ 1 - 0
Reachability Sample/Reachability Sample.xcodeproj/project.pbxproj

@@ -159,6 +159,7 @@
 		CA25599119D0D1990073826D /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
+				LastSwiftUpdateCheck = 0700;
 				LastUpgradeCheck = 0600;
 				ORGANIZATIONNAME = "Joylord Systems";
 				TargetAttributes = {

+ 10 - 8
Reachability Sample/Reachability Sample/ViewController.swift

@@ -20,29 +20,31 @@ class ViewController: UIViewController {
         super.viewDidLoad()
 
         if (useClosures) {
-            reachability.whenReachable = { reachability in
+            reachability?.whenReachable = { reachability in
                 self.updateLabelColourWhenReachable(reachability)
             }
-            reachability.whenUnreachable = { reachability in
+            reachability?.whenUnreachable = { reachability in
                 self.updateLabelColourWhenNotReachable(reachability)
             }
         } else {
             NSNotificationCenter.defaultCenter().addObserver(self, selector: "reachabilityChanged:", name: ReachabilityChangedNotification, object: reachability)
         }
         
-        reachability.startNotifier()
+        reachability?.startNotifier()
         
         // Initial reachability check
-        if reachability.isReachable() {
-            updateLabelColourWhenReachable(reachability)
-        } else {
-            updateLabelColourWhenNotReachable(reachability)
+        if let reachability = reachability {
+            if reachability.isReachable() {
+                updateLabelColourWhenReachable(reachability)
+            } else {
+                updateLabelColourWhenNotReachable(reachability)
+            }
         }
     }
     
     deinit {
 
-        reachability.stopNotifier()
+        reachability?.stopNotifier()
         
         if (!useClosures) {
             NSNotificationCenter.defaultCenter().removeObserver(self, name: ReachabilityChangedNotification, object: nil)

+ 61 - 72
Reachability.swift

@@ -30,12 +30,18 @@ import Foundation
 
 public let ReachabilityChangedNotification = "ReachabilityChangedNotification"
 
-public class Reachability: NSObject, Printable {
+func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) {
+    let reachability = unsafeBitCast(info.memory, Reachability.self)
+    reachability.reachabilityChanged(flags)
+}
+
+
+public class Reachability: NSObject {
 
     public typealias NetworkReachable = (Reachability) -> ()
     public typealias NetworkUneachable = (Reachability) -> ()
 
-    public enum NetworkStatus: Printable {
+    public enum NetworkStatus: CustomStringConvertible {
 
         case NotReachable, ReachableViaWiFi, ReachableViaWWAN
 
@@ -77,29 +83,32 @@ public class Reachability: NSObject, Printable {
 
     // MARK: - *** Initialisation methods ***
     
-    public required init(reachabilityRef: SCNetworkReachability) {
+    required public init?(reachabilityRef: SCNetworkReachability?) {
         reachableOnWWAN = true
         self.reachabilityRef = reachabilityRef
     }
     
-    public convenience init(hostname: String) {
-        let ref = SCNetworkReachabilityCreateWithName(nil, (hostname as NSString).UTF8String).takeRetainedValue()
+    public convenience init?(hostname: String) {
+        
+        let nodename = (hostname as NSString).UTF8String
+        let ref = SCNetworkReachabilityCreateWithName(nil, nodename)
         self.init(reachabilityRef: ref)
     }
 
-    public 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))
+        var zeroAddress = sockaddr_in()
         zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
         zeroAddress.sin_family = sa_family_t(AF_INET)
-
+        
         let ref = withUnsafePointer(&zeroAddress) {
-            SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, UnsafePointer($0)).takeRetainedValue()
+            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
         }
+        
         return Reachability(reachabilityRef: ref)
     }
 
-    public 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))
@@ -110,7 +119,7 @@ public class Reachability: NSObject, Printable {
         localWifiAddress.sin_addr.s_addr = in_addr_t(address.bigEndian)
 
         let ref = withUnsafePointer(&localWifiAddress) {
-            SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, UnsafePointer($0)).takeRetainedValue()
+            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
         }
         return Reachability(reachabilityRef: ref)
     }
@@ -118,34 +127,22 @@ public class Reachability: NSObject, Printable {
     // MARK: - *** Notifier methods ***
     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
+        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
+        
+        var s = self
+        withUnsafeMutablePointer(&s) {
+            context.info = UnsafeMutablePointer($0)
         }
+        
+        return SCNetworkReachabilitySetCallback(reachabilityRef!, callback, &context) != 0
     }
+    
 
     public func stopNotifier() {
 
-        reachabilityObject = nil
-
-        if let timer = dispatch_timer {
-            dispatch_source_cancel(timer)
-            dispatch_timer = nil
-        }
+        SCNetworkReachabilitySetCallback(reachabilityRef!, nil, nil)
 
+        reachabilityRef = nil
     }
 
     // MARK: - *** Connection test methods ***
@@ -203,24 +200,6 @@ public class Reachability: NSObject, Printable {
         }()
 
     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?
-
-    func timerFired() {
-        let currentReachabilityFlags = reachabilityFlags
-        if let _previousReachabilityFlags = previousReachabilityFlags {
-            if currentReachabilityFlags != previousReachabilityFlags {
-                dispatch_async(dispatch_get_main_queue(), { [unowned self] in
-                    self.reachabilityChanged(currentReachabilityFlags)
-                    self.previousReachabilityFlags = currentReachabilityFlags
-                    })
-            }
-        }
-    }
 
     private func reachabilityChanged(flags: SCNetworkReachabilityFlags) {
         if isReachableWithFlags(flags) {
@@ -259,10 +238,18 @@ public class Reachability: NSObject, Printable {
     }
 
     private func isReachableWithTest(test: (SCNetworkReachabilityFlags) -> (Bool)) -> Bool {
-        var flags: SCNetworkReachabilityFlags = 0
-        let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, &flags) != 0
-        if gotFlags {
-            return test(flags)
+        print("\(test)")
+
+        if let reachabilityRef = reachabilityRef {
+            
+            var flags = SCNetworkReachabilityFlags(rawValue: 0)
+            let gotFlags = withUnsafeMutablePointer(&flags) {
+                SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0))
+            }
+            
+            if gotFlags != 0 {
+                return test(flags)
+            }
         }
 
         return false
@@ -296,51 +283,53 @@ public class Reachability: NSObject, Printable {
 
     private func isOnWWAN(flags: SCNetworkReachabilityFlags) -> Bool {
         #if os(iOS)
-            return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsWWAN) != 0
+            return flags.contains(.IsWWAN)
         #else
             return false
         #endif
     }
     private func isReachable(flags: SCNetworkReachabilityFlags) -> Bool {
-        return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsReachable) != 0
+        return flags.contains(.Reachable)
     }
     private func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
-        return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionRequired) != 0
+        return flags.contains(.ConnectionRequired)
     }
     private func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
-        return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsInterventionRequired) != 0
+        return flags.contains(.InterventionRequired)
     }
     private func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool {
-        return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0
+        return flags.contains(.ConnectionOnTraffic)
     }
     private func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool {
-        return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnDemand) != 0
+        return flags.contains(.ConnectionOnDemand)
     }
     func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool {
-        return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand) != 0
+        return !flags.intersect([.ConnectionOnTraffic, .ConnectionOnDemand]).isEmpty
     }
     private func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool {
-        return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsTransientConnection) != 0
+        return flags.contains(.TransientConnection)
     }
     private func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool {
-        return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsLocalAddress) != 0
+        return flags.contains(.IsLocalAddress)
     }
     private func isDirect(flags: SCNetworkReachabilityFlags) -> Bool {
-        return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsDirect) != 0
+        return flags.contains(.IsDirect)
     }
     private func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool {
-        let testcase = SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
-        return flags & testcase == testcase
+        let testcase:SCNetworkReachabilityFlags = [.ConnectionRequired, .TransientConnection]
+        return flags.intersect(testcase) == testcase
     }
 
     private var reachabilityFlags: SCNetworkReachabilityFlags {
-        var flags: SCNetworkReachabilityFlags = 0
-        let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, &flags) != 0
-        if gotFlags {
-            return flags
+        if let reachabilityRef = reachabilityRef {
+            let flags = UnsafeMutablePointer<SCNetworkReachabilityFlags>()
+            let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, flags) != 0
+            if gotFlags {
+                return flags.memory
+            }
         }
 
-        return 0
+        return []
     }
 
     override public var description: String {