mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
feat: added Reachability plugin and moved Network module to it
This commit is contained in:
118
Kit/plugins/Reachability.swift
Normal file
118
Kit/plugins/Reachability.swift
Normal file
@@ -0,0 +1,118 @@
|
||||
//
|
||||
// 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")
|
||||
private let log: NextLog = NextLog.shared.copy(category: "Reachability")
|
||||
|
||||
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) -> Void 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@
|
||||
import Cocoa
|
||||
import Kit
|
||||
import SystemConfiguration
|
||||
import Reachability
|
||||
import CoreWLAN
|
||||
|
||||
struct ipResponse: Decodable {
|
||||
@@ -22,7 +21,7 @@ struct ipResponse: Decodable {
|
||||
}
|
||||
|
||||
internal class UsageReader: Reader<Network_Usage> {
|
||||
private var reachability: Reachability? = nil
|
||||
private var reachability: Reachability = Reachability(start: true)
|
||||
private var usage: Network_Usage = Network_Usage()
|
||||
|
||||
private var primaryInterface: String {
|
||||
@@ -50,19 +49,12 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
}
|
||||
|
||||
public override func setup() {
|
||||
do {
|
||||
self.reachability = try Reachability()
|
||||
try self.reachability!.startNotifier()
|
||||
} catch let err {
|
||||
error("initialize Reachability error \(err)", log: self.log)
|
||||
}
|
||||
|
||||
self.reachability!.whenReachable = { _ in
|
||||
self.reachability.reachable = {
|
||||
if self.active {
|
||||
self.getDetails()
|
||||
}
|
||||
}
|
||||
self.reachability!.whenUnreachable = { _ in
|
||||
self.reachability.unreachable = {
|
||||
if self.active {
|
||||
self.usage.reset()
|
||||
self.callback(self.usage)
|
||||
@@ -73,6 +65,10 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(resetTotalNetworkUsage), name: .resetTotalNetworkUsage, object: nil)
|
||||
}
|
||||
|
||||
public override func terminate() {
|
||||
self.reachability.stop()
|
||||
}
|
||||
|
||||
public override func read() {
|
||||
let current: Bandwidth = self.reader == "interface" ? self.readInterfaceBandwidth() : self.readProcessBandwidth()
|
||||
|
||||
@@ -90,7 +86,7 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
self.usage.total.upload += self.usage.bandwidth.upload
|
||||
self.usage.total.download += self.usage.bandwidth.download
|
||||
|
||||
self.usage.status = self.reachability?.connection != Optional.none
|
||||
self.usage.status = self.reachability.isReachable
|
||||
|
||||
self.callback(self.usage)
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
9A11AB26266FD828000C1C05 /* config.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9A11AB25266FD828000C1C05 /* config.plist */; };
|
||||
9A11AB36266FD9F4000C1C05 /* readers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A11AB35266FD9F4000C1C05 /* readers.swift */; };
|
||||
9A11AB67266FDB69000C1C05 /* Kit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A2846F72666A9CC00EC1F6D /* Kit.framework */; };
|
||||
9A27D5352538A456001BB651 /* Reachability in Frameworks */ = {isa = PBXBuildFile; productRef = 9A27D5342538A456001BB651 /* Reachability */; };
|
||||
9A2846FE2666A9CC00EC1F6D /* Kit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A2846F72666A9CC00EC1F6D /* Kit.framework */; };
|
||||
9A2846FF2666A9CC00EC1F6D /* Kit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9A2846F72666A9CC00EC1F6D /* Kit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
9A28475F2666AA2700EC1F6D /* LineChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2847552666AA2700EC1F6D /* LineChart.swift */; };
|
||||
@@ -69,6 +68,7 @@
|
||||
9A58DE9E24B363D800716A9F /* popup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58DE9D24B363D800716A9F /* popup.swift */; };
|
||||
9A58DEA024B363F300716A9F /* settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58DE9F24B363F300716A9F /* settings.swift */; };
|
||||
9A58DEA424B3647600716A9F /* settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58DEA324B3647600716A9F /* settings.swift */; };
|
||||
9A5A8447271895B700BC40A4 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5A8446271895B700BC40A4 /* Reachability.swift */; };
|
||||
9A5AF11B2469CE9B00684737 /* popup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5AF11A2469CE9B00684737 /* popup.swift */; };
|
||||
9A65654A253F20EF0096B607 /* settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A656549253F20EF0096B607 /* settings.swift */; };
|
||||
9A656562253F788A0096B607 /* popup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A656561253F788A0096B607 /* popup.swift */; };
|
||||
@@ -393,6 +393,7 @@
|
||||
9A58DE9D24B363D800716A9F /* popup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = popup.swift; sourceTree = "<group>"; };
|
||||
9A58DE9F24B363F300716A9F /* settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = settings.swift; sourceTree = "<group>"; };
|
||||
9A58DEA324B3647600716A9F /* settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = settings.swift; sourceTree = "<group>"; };
|
||||
9A5A8446271895B700BC40A4 /* Reachability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = "<group>"; };
|
||||
9A5AF11A2469CE9B00684737 /* popup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = popup.swift; sourceTree = "<group>"; };
|
||||
9A5F0503256A9135002FF75F /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
9A656549253F20EF0096B607 /* settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = settings.swift; sourceTree = "<group>"; };
|
||||
@@ -516,7 +517,6 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9A27D5352538A456001BB651 /* Reachability in Frameworks */,
|
||||
9A2847D62666AA9C00EC1F6D /* Kit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -826,6 +826,7 @@
|
||||
9A2848072666AB3000EC1F6D /* Updater.swift */,
|
||||
9A6EEBBD2685259500897371 /* Logger.swift */,
|
||||
9A8AE0A226921A2A00B13054 /* Server.swift */,
|
||||
9A5A8446271895B700BC40A4 /* Reachability.swift */,
|
||||
);
|
||||
path = plugins;
|
||||
sourceTree = "<group>";
|
||||
@@ -1080,7 +1081,6 @@
|
||||
);
|
||||
name = Net;
|
||||
packageProductDependencies = (
|
||||
9A27D5342538A456001BB651 /* Reachability */,
|
||||
);
|
||||
productName = Net;
|
||||
productReference = 9A3E17CC247A94AF00449CD1 /* Net.framework */;
|
||||
@@ -1340,7 +1340,6 @@
|
||||
);
|
||||
mainGroup = 9A1410EC229E721100D29793;
|
||||
packageReferences = (
|
||||
9A27D4D42538A37D001BB651 /* XCRemoteSwiftPackageReference "Reachability" */,
|
||||
9A27D4D72538A38A001BB651 /* XCRemoteSwiftPackageReference "Repeat" */,
|
||||
);
|
||||
productRefGroup = 9A1410F6229E721100D29793 /* Products */;
|
||||
@@ -1528,6 +1527,7 @@
|
||||
9A28477A2666AA5000EC1F6D /* settings.swift in Sources */,
|
||||
9A28475F2666AA2700EC1F6D /* LineChart.swift in Sources */,
|
||||
9A28480E2666AB3000EC1F6D /* Updater.swift in Sources */,
|
||||
9A5A8447271895B700BC40A4 /* Reachability.swift in Sources */,
|
||||
9A2847622666AA2700EC1F6D /* Label.swift in Sources */,
|
||||
9A28477C2666AA5000EC1F6D /* reader.swift in Sources */,
|
||||
9A9B8C9D27149A3700218374 /* Tachometer.swift in Sources */,
|
||||
@@ -2836,14 +2836,6 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
9A27D4D42538A37D001BB651 /* XCRemoteSwiftPackageReference "Reachability" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/ashleymills/Reachability.swift";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 5.1.0;
|
||||
};
|
||||
};
|
||||
9A27D4D72538A38A001BB651 /* XCRemoteSwiftPackageReference "Repeat" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/malcommac/Repeat";
|
||||
@@ -2855,11 +2847,6 @@
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
9A27D5342538A456001BB651 /* Reachability */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 9A27D4D42538A37D001BB651 /* XCRemoteSwiftPackageReference "Reachability" */;
|
||||
productName = Reachability;
|
||||
};
|
||||
9A2847AA2666AA7B00EC1F6D /* Repeat */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 9A27D4D72538A38A001BB651 /* XCRemoteSwiftPackageReference "Repeat" */;
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "Reachability",
|
||||
"repositoryURL": "https://github.com/ashleymills/Reachability.swift",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "c01bbdf2d633cf049ae1ed1a68a2020a8bda32e2",
|
||||
"version": "5.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Repeat",
|
||||
"repositoryURL": "https://github.com/malcommac/Repeat",
|
||||
|
||||
@@ -89,13 +89,6 @@
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<AdditionalOptions>
|
||||
<AdditionalOption
|
||||
key = "NSZombieEnabled"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Debug"
|
||||
|
||||
Reference in New Issue
Block a user