Files
Schedule-ICTIS/Carthage/Checkouts/TrueTime.swift/Sources/Reachability.swift
Vladimir Dubovik 5abafda21b Commit
2025-06-13 11:59:16 +03:00

113 lines
3.6 KiB
Swift

//
// Reachability.swift
// TrueTime
//
// Created by Michael Sanders on 7/21/16.
// Copyright © 2016 Instacart. All rights reserved.
//
import Foundation
import SystemConfiguration
enum ReachabilityStatus {
case notReachable
case reachableViaWWAN
case reachableViaWiFi
}
final class Reachability {
var callback: ((ReachabilityStatus) -> Void)?
var callbackQueue: DispatchQueue = .main
var status: ReachabilityStatus? {
if let networkReachability = self.networkReachability {
var flags = SCNetworkReachabilityFlags()
if SCNetworkReachabilityGetFlags(networkReachability, &flags) {
return ReachabilityStatus(flags)
}
}
return nil
}
var online: Bool {
return status != nil && status != .notReachable
}
deinit {
stopMonitoring()
}
func startMonitoring() {
var address = sockaddr_in()
address.sin_len = UInt8(MemoryLayout.size(ofValue: address))
address.sin_family = sa_family_t(AF_INET)
networkReachability = withUnsafePointer(to: &address) { pointer in
pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout<sockaddr>.size) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
}
guard let networkReachability = networkReachability else {
assertionFailure("SCNetworkReachabilityCreateWithAddress returned NULL")
return
}
var context = SCNetworkReachabilityContext(
version: 0,
info: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()),
retain: nil,
release: nil,
copyDescription: nil
)
SCNetworkReachabilitySetCallback(networkReachability, Reachability.reachabilityCallback, &context)
SCNetworkReachabilitySetDispatchQueue(networkReachability, .global())
if let status = status {
updateStatus(status)
}
}
func stopMonitoring() {
if let networkReachability = networkReachability {
SCNetworkReachabilitySetCallback(networkReachability, nil, nil)
SCNetworkReachabilitySetDispatchQueue(networkReachability, nil)
self.networkReachability = nil
}
}
private var networkReachability: SCNetworkReachability?
private static let reachabilityCallback: SCNetworkReachabilityCallBack = { _, flags, info in
guard let info = info else { return }
let reachability = Unmanaged<Reachability>.fromOpaque(info).takeUnretainedValue()
reachability.updateStatus(ReachabilityStatus(flags))
}
}
private extension Reachability {
func updateStatus(_ status: ReachabilityStatus) {
callbackQueue.async {
self.callback?(status)
}
}
}
private extension ReachabilityStatus {
init(_ flags: SCNetworkReachabilityFlags) {
let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)
let connectsAutomatically = flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic)
let connectsWithoutInteraction = connectsAutomatically && !flags.contains(.interventionRequired)
let isNetworkReachable = isReachable && (!needsConnection || connectsWithoutInteraction)
if !isNetworkReachable {
self = .notReachable
} else {
#if os(iOS)
if flags.contains(.isWWAN) {
self = .reachableViaWWAN
return
}
#endif
self = .reachableViaWiFi
}
}
}