Last active
October 20, 2015 02:04
-
-
Save dorentus/5cac0c09086ce48e2a12 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Reachability.swift | |
// | |
// Created by Zhang Yi on 15/7/29. | |
// Copyright (c) 2015 Zhang Yi <[email protected]>. All rights reserved. | |
// | |
// Licence: MIT | |
// | |
import Foundation | |
import SystemConfiguration | |
enum NetworkStatus { | |
case NotReachable | |
case ReachableViaWiFi | |
case ReachableViaWWAN | |
} | |
extension NetworkStatus { | |
var isReachable: Bool { | |
switch self { | |
case .NotReachable: return false | |
default: return true | |
} | |
} | |
var isReachableViaWWAN: Bool { | |
return self == .ReachableViaWWAN | |
} | |
var isReachableViaWiFi: Bool { | |
return self == .ReachableViaWiFi | |
} | |
} | |
extension NetworkStatus: CustomStringConvertible { | |
var description: String { | |
switch self { | |
case .NotReachable: return NSLocalizedString("No Connection", comment: "Network: No Connection") | |
case .ReachableViaWiFi: return NSLocalizedString("WiFi", comment: "Network: Using WiFi") | |
case .ReachableViaWWAN: return NSLocalizedString("Cellular", comment: "Network: Using Cellular (3g, 4g, etc.)") | |
} | |
} | |
} | |
extension SCNetworkReachabilityFlags { | |
var networkStatus: NetworkStatus { | |
let flag = self.rawValue | |
if flag & SCNetworkReachabilityFlags.Reachable.rawValue == 0 { | |
return .NotReachable | |
} | |
if flag & SCNetworkReachabilityFlags.IsWWAN.rawValue != 0 { | |
return .ReachableViaWWAN | |
} | |
return .ReachableViaWiFi | |
} | |
} | |
final class Reachability { | |
private let reachability: SCNetworkReachability | |
private let queue = dispatch_queue_create("today.rubyist.reachability", nil) | |
var reachableBlock: Optional<(reachability: Reachability) -> ()> | |
var unreachableBlock: Optional<(reachability: Reachability) -> ()> | |
var reachabilityBlock: Optional<(reachability: Reachability, flags: SCNetworkReachabilityFlags) -> ()> | |
init(reachability: SCNetworkReachability) { | |
self.reachability = reachability | |
} | |
func startNotifier() -> Bool { | |
let callback: @convention(block) (SCNetworkReachabilityRef, SCNetworkReachabilityFlags, UnsafePointer<()>) -> () = { [weak self] (reachability, flags, info) -> () in | |
if let r = self { | |
if flags.networkStatus.isReachable { | |
r.reachableBlock?(reachability: r) | |
} | |
else { | |
r.unreachableBlock?(reachability: r) | |
} | |
r.reachabilityBlock?(reachability: r, flags: flags) | |
} | |
dispatch_async(dispatch_get_main_queue()) { | |
NSNotificationCenter.defaultCenter().postNotificationName(Reachability.ReachabilityChangedNotificationName, object: self) | |
} | |
} | |
let blockObject = imp_implementationWithBlock(unsafeBitCast(callback, AnyObject.self)) | |
let fp = unsafeBitCast(blockObject, SCNetworkReachabilityCallBack.self) | |
var context = SCNetworkReachabilityContext() | |
if SCNetworkReachabilitySetCallback(reachability, fp, &context) { | |
if SCNetworkReachabilitySetDispatchQueue(reachability, queue) { | |
return true | |
} | |
SCNetworkReachabilitySetCallback(reachability, nil, nil) | |
} | |
return false | |
} | |
func stopNotifier() { | |
SCNetworkReachabilitySetCallback(reachability, nil, nil) | |
SCNetworkReachabilitySetDispatchQueue(reachability, nil) | |
} | |
deinit { | |
stopNotifier() | |
} | |
} | |
extension Reachability { | |
convenience init(hostname: String) { | |
let reachability = SCNetworkReachabilityCreateWithName(nil, hostname.cStringUsingEncoding(NSUTF8StringEncoding)!) | |
self.init(reachability: reachability!) | |
} | |
convenience init(address: sockaddr_in) { | |
var address = unsafeBitCast(address, sockaddr.self) | |
let reachability = SCNetworkReachabilityCreateWithAddress(nil, &address) | |
self.init(reachability: reachability!) | |
} | |
} | |
extension Reachability { | |
static let ReachabilityChangedNotificationName = "ReachabilityChangedNotification" | |
static let reachabilityForInternetConnection: Reachability = { | |
var zeroAddress = sockaddr_in() | |
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress)) | |
zeroAddress.sin_family = sa_family_t(AF_INET) | |
return Reachability(address: zeroAddress) | |
}() | |
static let reachabilityForLocalWiFi: Reachability = { | |
var address = sockaddr_in() | |
address.sin_len = UInt8(sizeofValue(address)) | |
address.sin_family = sa_family_t(AF_INET) | |
let localNet: UInt32 = 0xA9FE0000 // IN_LINKLOCALNETNUM aka. 169.254.0.0 | |
address.sin_addr.s_addr = in_addr_t(localNet.bigEndian) | |
return Reachability(address: address) | |
}() | |
} | |
extension Reachability { | |
var currentReachabilityFlags: SCNetworkReachabilityFlags? { | |
var flags = SCNetworkReachabilityFlags(rawValue: 0) | |
if SCNetworkReachabilityGetFlags(self.reachability, &flags) { | |
return flags | |
} | |
return nil | |
} | |
var currentReachabilityStatus: NetworkStatus? { | |
return currentReachabilityFlags?.networkStatus | |
} | |
} | |
extension Reachability { | |
var isReachable: Bool { | |
return currentReachabilityStatus?.isReachable ?? false | |
} | |
var isReachableViaWWAN: Bool { | |
return currentReachabilityStatus?.isReachableViaWWAN ?? false | |
} | |
var isReachableViaWiFi: Bool { | |
return currentReachabilityStatus?.isReachableViaWiFi ?? false | |
} | |
} | |
extension Reachability { | |
var isConnectionRequired: Bool { | |
if let flags = currentReachabilityFlags { | |
return flags.rawValue & SCNetworkReachabilityFlags.ConnectionRequired.rawValue != 0 | |
} | |
return false | |
} | |
var isConnectionOnDemand: Bool { | |
if let flags = currentReachabilityFlags { | |
return (flags.rawValue & SCNetworkReachabilityFlags.ConnectionRequired.rawValue != 0) | |
&& (flags.rawValue & (SCNetworkReachabilityFlags.ConnectionOnTraffic.rawValue | SCNetworkReachabilityFlags.ConnectionOnDemand.rawValue) != 0) | |
} | |
return false | |
} | |
var isInterventionRequired: Bool { | |
if let flags = currentReachabilityFlags { | |
return (flags.rawValue & SCNetworkReachabilityFlags.ConnectionRequired.rawValue != 0) | |
&& (flags.rawValue & SCNetworkReachabilityFlags.InterventionRequired.rawValue != 0) | |
} | |
return false | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment