Skip to content

Commit

Permalink
feat: added macOS widget for Network module
Browse files Browse the repository at this point in the history
  • Loading branch information
exelban committed Aug 1, 2024
1 parent 442e064 commit d0f4354
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Modules/Net/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import Cocoa
import Kit
import SystemConfiguration
import WidgetKit

public enum Network_t: String, Codable {
case wifi
Expand Down Expand Up @@ -235,6 +236,12 @@ public class Network: Module {
default: break
}
}

if #available(macOS 11.0, *) {
guard let blobData = try? JSONEncoder().encode(raw) else { return }
self.userDefaults?.set(blobData, forKey: "Network@UsageReader")
WidgetCenter.shared.reloadTimelines(ofKind: Network_entry.kind)
}
}

private func connectivityCallback(_ raw: Network_Connectivity?) {
Expand Down
121 changes: 121 additions & 0 deletions Modules/Net/widget.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//
// widget.swift
// Net
//
// Created by Serhiy Mytrovtsiy on 30/07/2024
// Using Swift 5.0
// Running on macOS 14.5
//
// Copyright © 2024 Serhiy Mytrovtsiy. All rights reserved.
//

import SwiftUI
import WidgetKit
import Charts
import Kit

public struct Network_entry: TimelineEntry {
public static let kind = "NetworkWidget"
public static var snapshot: Network_entry = Network_entry()

public var date: Date {
Calendar.current.date(byAdding: .second, value: 5, to: Date())!
}
public var value: Network_Usage? = nil
}

@available(macOS 11.0, *)
public struct Provider: TimelineProvider {
public typealias Entry = Network_entry

private let userDefaults: UserDefaults? = UserDefaults(suiteName: "eu.exelban.Stats.widgets")

public func placeholder(in context: Context) -> Network_entry {
Network_entry()
}

public func getSnapshot(in context: Context, completion: @escaping (Network_entry) -> Void) {
completion(Network_entry.snapshot)
}

public func getTimeline(in context: Context, completion: @escaping (Timeline<Network_entry>) -> Void) {
var entry = Network_entry()
if let raw = userDefaults?.data(forKey: "Network@UsageReader"), let load = try? JSONDecoder().decode(Network_Usage.self, from: raw) {
entry.value = load
}
let entries: [Network_entry] = [entry]
completion(Timeline(entries: entries, policy: .atEnd))
}
}

@available(macOS 14.0, *)
public struct NetworkWidget: Widget {
private var downloadColor: Color = Color(nsColor: NSColor.systemBlue)
private var uploadColor: Color = Color(nsColor: NSColor.systemRed)

public init() {}

public var body: some WidgetConfiguration {
StaticConfiguration(kind: Network_entry.kind, provider: Provider()) { entry in
VStack(spacing: 10) {
if let value = entry.value {
VStack {
HStack {
VStack {
VStack(spacing: 0) {
Text(Units(bytes: value.bandwidth.download).getReadableTuple().0).font(.system(size: 24, weight: .regular))
Text(Units(bytes: value.bandwidth.download).getReadableTuple().1).font(.system(size: 10, weight: .regular))
}
Text("Download").font(.system(size: 12, weight: .regular)).foregroundColor(.gray)
}.frame(maxWidth: .infinity)
VStack {
VStack(spacing: 0) {
Text(Units(bytes: value.bandwidth.upload).getReadableTuple().0).font(.system(size: 24, weight: .regular))
Text(Units(bytes: value.bandwidth.upload).getReadableTuple().1).font(.system(size: 10, weight: .regular))
}
Text("Upload").font(.system(size: 12, weight: .regular)).foregroundColor(.gray)
}.frame(maxWidth: .infinity)
}
.frame(maxHeight: .infinity)
VStack(spacing: 3) {
HStack {
Text("Status").font(.system(size: 12, weight: .regular)).foregroundColor(.secondary)
Spacer()
Text(value.status ? "UP" : "DOWN")
}
if let interface = value.interface {
HStack {
Text("Interface").font(.system(size: 12, weight: .regular)).foregroundColor(.secondary)
Spacer()
Text(value.wifiDetails.ssid ?? interface.displayName)
}
}
HStack {
Text("IP").font(.system(size: 12, weight: .regular)).foregroundColor(.secondary)
Spacer()
if let raddr = value.raddr.v6 {
Text(raddr)
.font(.system(size: 8))
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity, alignment: .center)
} else if let raddr = value.raddr.v4 {
Text(raddr)
} else {
Text("Unknown")
}
}
}
}
} else {
Text("No data")
}
}
.containerBackground(for: .widget) {
Color.clear
}
}
.configurationDisplayName("Network widget")
.description("Displays network stats")
.supportedFamilies([.systemSmall])
}
}
32 changes: 32 additions & 0 deletions Stats.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
5C4E8BE92B71031A00F148B6 /* Kit.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C4E8BE82B7102A700F148B6 /* Kit.h */; settings = {ATTRIBUTES = (Private, ); }; };
5C5647F82A3F6B100098FFE9 /* Telemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5647F72A3F6B100098FFE9 /* Telemetry.swift */; };
5C621D822B4770D6004ED7AF /* process.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C621D812B4770D6004ED7AF /* process.swift */; };
5C645BFF2C591F6600D8342A /* widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C645BFE2C591F6600D8342A /* widget.swift */; };
5C645C002C591FFA00D8342A /* Net.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A3E17CC247A94AF00449CD1 /* Net.framework */; };
5C645C012C591FFA00D8342A /* Net.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9A3E17CC247A94AF00449CD1 /* Net.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5C7C1DF42C29A3A00060387D /* notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C1DF32C29A3A00060387D /* notifications.swift */; };
5C8E001029269C7F0027C75A /* protocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE493829265055000F2856 /* protocol.swift */; };
5CA518382B543FE600EBCCC4 /* portal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA518372B543FE600EBCCC4 /* portal.swift */; };
Expand Down Expand Up @@ -238,6 +241,13 @@
remoteGlobalIDString = 9A2846F62666A9CC00EC1F6D;
remoteInfo = Kit;
};
5C645C022C591FFA00D8342A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9A1410ED229E721100D29793 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 9A3E17CB247A94AF00449CD1;
remoteInfo = Net;
};
5CE7E79A2C318513006BC92C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9A1410ED229E721100D29793 /* Project object */;
Expand Down Expand Up @@ -374,6 +384,17 @@
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
5C645C042C591FFA00D8342A /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
5C645C012C591FFA00D8342A /* Net.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
5CE7E79D2C318513006BC92C /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
Expand Down Expand Up @@ -509,6 +530,7 @@
5C4E8BE82B7102A700F148B6 /* Kit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Kit.h; sourceTree = "<group>"; };
5C5647F72A3F6B100098FFE9 /* Telemetry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Telemetry.swift; sourceTree = "<group>"; };
5C621D812B4770D6004ED7AF /* process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = process.swift; sourceTree = "<group>"; };
5C645BFE2C591F6600D8342A /* widget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = widget.swift; sourceTree = "<group>"; };
5C7C1DF32C29A3A00060387D /* notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = notifications.swift; sourceTree = "<group>"; };
5C9F90A02A76B30500D41748 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = "<group>"; };
5CA518372B543FE600EBCCC4 /* portal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = portal.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -697,6 +719,7 @@
5CE7E78E2C318512006BC92C /* SwiftUI.framework in Frameworks */,
5C0A9CAA2C46838A00EE6A89 /* GPU.framework in Frameworks */,
5CE7E78C2C318512006BC92C /* WidgetKit.framework in Frameworks */,
5C645C002C591FFA00D8342A /* Net.framework in Frameworks */,
5C0A9CB42C46839500EE6A89 /* Disk.framework in Frameworks */,
5C0A9CA52C46838300EE6A89 /* CPU.framework in Frameworks */,
5C0A9CAF2C46838F00EE6A89 /* RAM.framework in Frameworks */,
Expand Down Expand Up @@ -1041,6 +1064,7 @@
9A3E17E9247B07BF00449CD1 /* popup.swift */,
5C23BC0B29A10BE000DBA990 /* portal.swift */,
9A58DEA324B3647600716A9F /* settings.swift */,
5C645BFE2C591F6600D8342A /* widget.swift */,
9A3E17CF247A94AF00449CD1 /* Info.plist */,
9A3E17DC247A94C300449CD1 /* config.plist */,
);
Expand Down Expand Up @@ -1364,6 +1388,7 @@
5CE7E7862C318512006BC92C /* Sources */,
5CE7E7872C318512006BC92C /* Frameworks */,
5CE7E7882C318512006BC92C /* Resources */,
5C645C042C591FFA00D8342A /* Embed Frameworks */,
);
buildRules = (
);
Expand All @@ -1372,6 +1397,7 @@
5C0A9CAD2C46838A00EE6A89 /* PBXTargetDependency */,
5C0A9CB22C46838F00EE6A89 /* PBXTargetDependency */,
5C0A9CB72C46839500EE6A89 /* PBXTargetDependency */,
5C645C032C591FFA00D8342A /* PBXTargetDependency */,
);
name = WidgetsExtension;
productName = WidgetsExtension;
Expand Down Expand Up @@ -2078,6 +2104,7 @@
9A3E17DB247A94BC00449CD1 /* readers.swift in Sources */,
9A3E17EA247B07BF00449CD1 /* popup.swift in Sources */,
5C23BC0C29A10BE000DBA990 /* portal.swift in Sources */,
5C645BFF2C591F6600D8342A /* widget.swift in Sources */,
9A58DEA424B3647600716A9F /* settings.swift in Sources */,
9A3E17D9247A94B500449CD1 /* main.swift in Sources */,
);
Expand Down Expand Up @@ -2219,6 +2246,11 @@
target = 9A2846F62666A9CC00EC1F6D /* Kit */;
targetProxy = 5C2229B229CDFBF600F00E69 /* PBXContainerItemProxy */;
};
5C645C032C591FFA00D8342A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 9A3E17CB247A94AF00449CD1 /* Net */;
targetProxy = 5C645C022C591FFA00D8342A /* PBXContainerItemProxy */;
};
5CE7E79B2C318513006BC92C /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5CE7E7892C318512006BC92C /* WidgetsExtension */;
Expand Down
2 changes: 2 additions & 0 deletions Widgets/widgets.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import CPU
import GPU
import RAM
import Disk
import Net

@main
struct WidgetsBundle: WidgetBundle {
Expand All @@ -23,5 +24,6 @@ struct WidgetsBundle: WidgetBundle {
GPUWidget()
RAMWidget()
DiskWidget()
NetworkWidget()
}
}

0 comments on commit d0f4354

Please sign in to comment.