Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
iOS Reachability Helper
import Foundation
import SystemConfiguration
extension Notification.Name {
public static let reachabilityChanged = Notification.Name(rawValue: "reachabilityChanged")
}
protocol ReachabilityObserver: class {
func reachabilityDidChange(_ reachability: Reachability)
}
fileprivate class ObserverBox: Equatable, Hashable {
let value: ReachabilityObserver
init(_ value: ReachabilityObserver) {
self.value = value
}
static func ==(lhs: ObserverBox, rhs: ObserverBox) -> Bool {
return lhs.value === rhs.value
}
var hashValue: Int {
return ObjectIdentifier(value).hashValue
}
}
public class Reachability: NSObject {
public static let shared = Reachability()
public var isReachable: Bool {
return flags?.contains(.reachable) == true
}
private var observers = Set<ObserverBox>()
public let host: String
func add(observer: ReachabilityObserver) {
let box = ObserverBox(observer)
observers.insert(box)
DispatchQueue.main.async {
observer.reachabilityDidChange(self)
}
}
func remove(observer: ReachabilityObserver) {
let box = ObserverBox(observer)
observers.remove(box)
}
var lastUpdated: Date = .distantPast
private let reachability: SCNetworkReachability?
private let isolationQueue = DispatchQueue(label: "reachability.flags")
private var _flags: SCNetworkReachabilityFlags?
public var flags: SCNetworkReachabilityFlags? {
get {
return isolationQueue.sync { _flags }
}
set {
lastUpdated = Date()
isolationQueue.async { [weak self, observers] in
guard let self = self else { return }
self._flags = newValue
NotificationCenter.default.post(name: .reachabilityChanged, object: nil)
DispatchQueue.main.async {
observers.forEach {
$0.value.reachabilityDidChange(self)
}
}
}
}
}
public init(host: String = "www.google.com") {
self.host = host
self.reachability = SCNetworkReachabilityCreateWithName(nil, host)
super.init()
guard let reachability = reachability else { return }
var flags = SCNetworkReachabilityFlags()
SCNetworkReachabilityGetFlags(reachability, &flags)
_flags = flags
let callback: SCNetworkReachabilityCallBack = { _, flags, infoPtr in
guard let info = infoPtr else { return }
let passedSelf = Unmanaged<Reachability>.fromOpaque(info).takeUnretainedValue()
passedSelf.flags = flags
}
let selfPointer = Unmanaged.passUnretained(self).toOpaque()
var context = SCNetworkReachabilityContext(version: 0, info: selfPointer, retain: nil, release: nil, copyDescription: nil)
SCNetworkReachabilitySetCallback(reachability, callback, &context)
SCNetworkReachabilitySetDispatchQueue(reachability, .main)
}
deinit {
guard let reachability = reachability else { return }
SCNetworkReachabilitySetDispatchQueue(reachability, nil)
}
}
extension SCNetworkReachabilityFlags {
var stringComponents: [String] {
var result = [String]()
if contains(.transientConnection) {
result.append("transient connection")
}
if contains(.reachable) {
result.append("reachable")
}
if contains(.connectionRequired) {
result.append("connection required")
}
if contains(.connectionOnTraffic) {
result.append("connection on traffic")
}
if contains(.interventionRequired) {
result.append("intervention required")
}
if contains(.connectionOnDemand) {
result.append("connection on demand")
}
if contains(.isLocalAddress) {
result.append("local address")
}
if contains(.isDirect) {
result.append("direct")
}
if contains(.isWWAN) {
result.append("WWAN")
}
if contains(.connectionAutomatic) {
result.append("connection automatic")
}
return result
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment