Skip to content

Instantly share code, notes, and snippets.

@dorentus
Last active October 20, 2015 02:04
Show Gist options
  • Save dorentus/5cac0c09086ce48e2a12 to your computer and use it in GitHub Desktop.
Save dorentus/5cac0c09086ce48e2a12 to your computer and use it in GitHub Desktop.
//
// Reachability.swift
//
// Created by Zhang Yi on 15/7/29.
// Copyright (c) 2015 Zhang Yi <zhangyi.cn@gmail.com>. 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