mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
118 lines
3.8 KiB
Swift
118 lines
3.8 KiB
Swift
//
|
|
// Reachability.swift
|
|
// Kit
|
|
//
|
|
// Created by Serhiy Mytrovtsiy on 15/10/2021.
|
|
// Using Swift 5.0.
|
|
// Running on macOS 10.15.
|
|
//
|
|
// Copyright © 2021 Serhiy Mytrovtsiy. All rights reserved.
|
|
// Inspired by https://gist.github.com/saeed-rz/d9827b312915e0dc145497532e514470 and https://github.com/ashleymills/Reachability.swift
|
|
|
|
import Foundation
|
|
import SystemConfiguration
|
|
|
|
public class Reachability {
|
|
public var isReachable: Bool = false
|
|
|
|
public var reachable: () -> Void = {}
|
|
public var unreachable: () -> Void = {}
|
|
|
|
private var isRunning = false
|
|
private var reachability: SCNetworkReachability?
|
|
private let reachabilitySerialQueue = DispatchQueue(label: "eu.exelban.ReachabilityQueue")
|
|
|
|
public init(start: Bool = false) {
|
|
var zeroAddress = sockaddr()
|
|
zeroAddress.sa_len = UInt8(MemoryLayout<sockaddr>.size)
|
|
zeroAddress.sa_family = sa_family_t(AF_INET)
|
|
|
|
guard let ref = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) else {
|
|
error("SCNetworkReachability create with address")
|
|
return
|
|
}
|
|
|
|
self.reachability = ref
|
|
|
|
if start {
|
|
self.start()
|
|
}
|
|
}
|
|
|
|
public func start() {
|
|
guard let reachability = self.reachability, !self.isRunning else {
|
|
error("reachability is nil or already started")
|
|
return
|
|
}
|
|
|
|
let callback: SCNetworkReachabilityCallBack = { (_, flags, info) in
|
|
guard let info = info else { return }
|
|
Unmanaged<Reachability>.fromOpaque(info).takeUnretainedValue().setFlags(flags)
|
|
}
|
|
|
|
var context = SCNetworkReachabilityContext(
|
|
version: 0,
|
|
info: Unmanaged<Reachability>.passUnretained(self).toOpaque(),
|
|
retain: { (info: UnsafeRawPointer) -> UnsafeRawPointer in
|
|
let unmanagedReachability = Unmanaged<Reachability>.fromOpaque(info)
|
|
_ = unmanagedReachability.retain()
|
|
return UnsafeRawPointer(unmanagedReachability.toOpaque())
|
|
},
|
|
release: { (info: UnsafeRawPointer) in
|
|
Unmanaged<Reachability>.fromOpaque(info).release()
|
|
},
|
|
copyDescription: nil
|
|
)
|
|
|
|
guard SCNetworkReachabilitySetCallback(reachability, callback, &context) else {
|
|
error("SCNetworkReachability set dispatch callback")
|
|
self.stop()
|
|
return
|
|
}
|
|
guard SCNetworkReachabilitySetDispatchQueue(reachability, reachabilitySerialQueue) else {
|
|
error("SCNetworkReachability set dispatch queue")
|
|
self.stop()
|
|
return
|
|
}
|
|
|
|
self.reachabilitySerialQueue.sync { [unowned self] in
|
|
guard let reachability = self.reachability else {
|
|
error("reachability is nil")
|
|
return
|
|
}
|
|
|
|
var flags = SCNetworkReachabilityFlags()
|
|
if !SCNetworkReachabilityGetFlags(reachability, &flags) {
|
|
error("SCNetworkReachability get flags")
|
|
self.stop()
|
|
return
|
|
}
|
|
|
|
self.setFlags(flags)
|
|
}
|
|
|
|
self.isRunning = true
|
|
}
|
|
|
|
public func stop() {
|
|
defer { self.isRunning = false }
|
|
guard let reachability = self.reachability, self.isRunning else {
|
|
error("reachability is nil or already stopped")
|
|
return
|
|
}
|
|
|
|
SCNetworkReachabilitySetCallback(reachability, nil, nil)
|
|
SCNetworkReachabilitySetDispatchQueue(reachability, nil)
|
|
}
|
|
|
|
private func setFlags(_ flags: SCNetworkReachabilityFlags) {
|
|
self.isReachable = flags.contains(.reachable)
|
|
|
|
if self.isReachable {
|
|
self.reachable()
|
|
} else {
|
|
self.unreachable()
|
|
}
|
|
}
|
|
}
|