Browse Source

Added NetworkReachabilityManager class to listen for reachability changes.

Christian Noon 10 years ago
parent
commit
e4d23880ab
2 changed files with 167 additions and 0 deletions
  1. 8 0
      Alamofire.xcodeproj/project.pbxproj
  2. 159 0
      Source/NetworkReachabilityManager.swift

+ 8 - 0
Alamofire.xcodeproj/project.pbxproj

@@ -30,6 +30,9 @@
 		4C33A1441B52089C00873DFF /* ServerTrustPolicyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C33A1421B52089C00873DFF /* ServerTrustPolicyTests.swift */; };
 		4C341BBA1B1A865A00C1B34D /* CacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C341BB91B1A865A00C1B34D /* CacheTests.swift */; };
 		4C341BBB1B1A865A00C1B34D /* CacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C341BB91B1A865A00C1B34D /* CacheTests.swift */; };
+		4C3D00541C66A63000D1F709 /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3D00531C66A63000D1F709 /* NetworkReachabilityManager.swift */; };
+		4C3D00551C66A63000D1F709 /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3D00531C66A63000D1F709 /* NetworkReachabilityManager.swift */; };
+		4C3D00561C66A63000D1F709 /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3D00531C66A63000D1F709 /* NetworkReachabilityManager.swift */; };
 		4C4CBE7B1BAF700C0024D659 /* String+AlamofireTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4CBE7A1BAF700C0024D659 /* String+AlamofireTests.swift */; };
 		4C4CBE7C1BAF700C0024D659 /* String+AlamofireTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4CBE7A1BAF700C0024D659 /* String+AlamofireTests.swift */; };
 		4C743CF61C22772D00BCB23E /* certDER.cer in Resources */ = {isa = PBXBuildFile; fileRef = B39E2F831C1A72F8002DA1A9 /* certDER.cer */; };
@@ -235,6 +238,7 @@
 		4C33A1241B5207DB00873DFF /* unicorn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unicorn.png; sourceTree = "<group>"; };
 		4C33A1421B52089C00873DFF /* ServerTrustPolicyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerTrustPolicyTests.swift; sourceTree = "<group>"; };
 		4C341BB91B1A865A00C1B34D /* CacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheTests.swift; sourceTree = "<group>"; };
+		4C3D00531C66A63000D1F709 /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkReachabilityManager.swift; sourceTree = "<group>"; };
 		4C4CBE7A1BAF700C0024D659 /* String+AlamofireTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+AlamofireTests.swift"; sourceTree = "<group>"; };
 		4C7C8D211B9D0D9000948136 /* NSURLSessionConfiguration+AlamofireTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSURLSessionConfiguration+AlamofireTests.swift"; sourceTree = "<group>"; };
 		4C811F8C1B51856D00E0F59A /* ServerTrustPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerTrustPolicy.swift; sourceTree = "<group>"; };
@@ -504,6 +508,7 @@
 			children = (
 				4CDE2C3C1AF89D4900BABAE5 /* Download.swift */,
 				4C23EB421B327C5B0090E0BC /* MultipartFormData.swift */,
+				4C3D00531C66A63000D1F709 /* NetworkReachabilityManager.swift */,
 				4CDE2C451AF89FF300BABAE5 /* ResponseSerialization.swift */,
 				4C811F8C1B51856D00E0F59A /* ServerTrustPolicy.swift */,
 				4C83F41A1B749E0E00203445 /* Stream.swift */,
@@ -949,6 +954,7 @@
 				4CF627091BA7CBF60011A099 /* Manager.swift in Sources */,
 				4CF6270F1BA7CBF60011A099 /* ResponseSerialization.swift in Sources */,
 				4CF6270B1BA7CBF60011A099 /* Request.swift in Sources */,
+				4C3D00561C66A63000D1F709 /* NetworkReachabilityManager.swift in Sources */,
 				4CF6270A1BA7CBF60011A099 /* ParameterEncoding.swift in Sources */,
 				4CF627101BA7CBF60011A099 /* ServerTrustPolicy.swift in Sources */,
 				4CF6270D1BA7CBF60011A099 /* Download.swift in Sources */,
@@ -995,6 +1001,7 @@
 				4DD67C251A5C590000ED2280 /* Alamofire.swift in Sources */,
 				4C23EB441B327C5B0090E0BC /* MultipartFormData.swift in Sources */,
 				4C811F8E1B51856D00E0F59A /* ServerTrustPolicy.swift in Sources */,
+				4C3D00551C66A63000D1F709 /* NetworkReachabilityManager.swift in Sources */,
 				4C83F41C1B749E0E00203445 /* Stream.swift in Sources */,
 				4CDE2C3E1AF89D4900BABAE5 /* Download.swift in Sources */,
 				4CDE2C441AF89F0900BABAE5 /* Validation.swift in Sources */,
@@ -1037,6 +1044,7 @@
 				F897FF4119AA800700AB5182 /* Alamofire.swift in Sources */,
 				4C23EB431B327C5B0090E0BC /* MultipartFormData.swift in Sources */,
 				4C811F8D1B51856D00E0F59A /* ServerTrustPolicy.swift in Sources */,
+				4C3D00541C66A63000D1F709 /* NetworkReachabilityManager.swift in Sources */,
 				4C83F41B1B749E0E00203445 /* Stream.swift in Sources */,
 				4CDE2C3D1AF89D4900BABAE5 /* Download.swift in Sources */,
 				4CDE2C431AF89F0900BABAE5 /* Validation.swift in Sources */,

+ 159 - 0
Source/NetworkReachabilityManager.swift

@@ -0,0 +1,159 @@
+// NetworkReachabilityManager.swift
+//
+// Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+import Foundation
+import SystemConfiguration
+
+public let NetworkReachabilityStatusDidChangeNotification = "com.alamofire.network.reachability.status.did.change"
+
+public enum NetworkReachabilityStatus: Int {
+    case Unknown         = -1
+    case NotReachable    = 0
+    case ReachableOnWWAN = 1
+    case ReachableOnWiFi = 2
+}
+
+public class NetworkReachabilityManager {
+    public typealias Listener = NetworkReachabilityStatus -> Void
+
+    // MARK: - Properties
+
+    public var isReachable: Bool { return isReachableOnWWAN || isReachableOnWiFi }
+    public var isReachableOnWWAN: Bool { return networkReachabilityStatus == .ReachableOnWWAN }
+    public var isReachableOnWiFi: Bool { return networkReachabilityStatus == .ReachableOnWiFi }
+
+    public var networkReachabilityStatus: NetworkReachabilityStatus {
+        guard let flags = self.flags else { return .Unknown }
+        return networkReachabilityStatusForFlags(flags)
+    }
+
+    public var listenerQueue: dispatch_queue_t = dispatch_get_main_queue()
+    public var listener: Listener?
+
+    private var flags: SCNetworkReachabilityFlags? {
+        var flags = SCNetworkReachabilityFlags()
+
+        if SCNetworkReachabilityGetFlags(reachability, &flags) {
+            return flags
+        }
+
+        return nil
+    }
+
+    private let reachability: SCNetworkReachability
+    private var previousFlags: SCNetworkReachabilityFlags
+
+    // MARK: - Initialization
+
+    public convenience init?(host: String) {
+        guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil }
+        self.init(reachability: reachability)
+    }
+
+    public convenience init?() {
+        var address = sockaddr_in6()
+        address.sin6_len = UInt8(sizeofValue(address))
+        address.sin6_family = sa_family_t(AF_INET6)
+
+        guard let reachability = withUnsafePointer(&address, {
+            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
+        }) else { return nil }
+
+        self.init(reachability: reachability)
+    }
+
+    private init(reachability: SCNetworkReachability) {
+        self.reachability = reachability
+        self.previousFlags = SCNetworkReachabilityFlags()
+    }
+
+    deinit {
+        stopListening()
+    }
+
+    // MARK: - Listening
+
+    public func startListening() -> Bool {
+        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
+        context.info = UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque())
+
+        let callbackEnabled = SCNetworkReachabilitySetCallback(
+            reachability,
+            { (_, flags, info) in
+                let reachability = Unmanaged<NetworkReachabilityManager>.fromOpaque(COpaquePointer(info)).takeUnretainedValue()
+                reachability.notifyListener(flags)
+            },
+            &context
+        )
+
+        let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue)
+
+        dispatch_async(listenerQueue) {
+            self.notifyListener(self.flags ?? SCNetworkReachabilityFlags())
+        }
+
+        return callbackEnabled && queueEnabled
+    }
+
+    public func stopListening() {
+        SCNetworkReachabilitySetCallback(reachability, nil, nil)
+        SCNetworkReachabilitySetDispatchQueue(reachability, nil)
+    }
+
+    // MARK: - Internal - Listener Notification
+
+    func notifyListener(flags: SCNetworkReachabilityFlags) {
+        guard previousFlags != flags else { return }
+        previousFlags = flags
+
+        let networkReachabilityStatus = networkReachabilityStatusForFlags(flags)
+
+        listener?(networkReachabilityStatus)
+
+        dispatch_async(dispatch_get_main_queue()) {
+            NSNotificationCenter.defaultCenter().postNotificationName(
+                NetworkReachabilityStatusDidChangeNotification,
+                object: networkReachabilityStatus.rawValue
+            )
+        }
+    }
+
+    // MARK: - Internal - Network Reachability Status
+
+    func networkReachabilityStatusForFlags(flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus {
+        guard flags.contains(.Reachable) else { return .NotReachable }
+
+        var networkStatus: NetworkReachabilityStatus = .NotReachable
+
+        if !flags.contains(.ConnectionRequired) { networkStatus = .ReachableOnWiFi }
+
+        if flags.contains(.ConnectionOnDemand) || flags.contains(.ConnectionOnTraffic) {
+            if !flags.contains(.InterventionRequired) { networkStatus = .ReachableOnWiFi }
+        }
+
+        #if os(iOS)
+            if flags.contains(.IsWWAN) { networkStatus = .ReachableOnWWAN }
+        #endif
+
+        return networkStatus
+    }
+}