Skip to content

Commit 47dec1d

Browse files
authored
Merge pull request #17 from Hamzenium/Added-Swift-UI-Support
Added Swift Support
2 parents c9c85b3 + 5e8391d commit 47dec1d

12 files changed

Lines changed: 325 additions & 254 deletions

File tree

.DS_Store

6 KB
Binary file not shown.

MySwiftApp/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc

MySwiftApp/Package.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// swift-tools-version:5.9
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "MySwiftApp",
6+
platforms: [
7+
.macOS(.v13)
8+
],
9+
products: [
10+
.executable(name: "MySwiftApp", targets: ["MySwiftApp"])
11+
],
12+
targets: [
13+
.executableTarget(
14+
name: "MySwiftApp",
15+
dependencies: [],
16+
path: "Sources"
17+
)
18+
]
19+
)

MySwiftApp/Sources/main.swift

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import SwiftUI
2+
import Combine
3+
4+
class ResourceMonitorViewModel: ObservableObject {
5+
@Published var cpuUsage: Double = 0
6+
@Published var memoryUsage: Double = 0
7+
@Published var diskUsage: Double = 0
8+
9+
@Published var alertsEnabled: Bool = true
10+
11+
@Published var cpuThreshold: String = ""
12+
@Published var memoryThreshold: String = ""
13+
@Published var diskThreshold: String = ""
14+
15+
private var timer: AnyCancellable?
16+
17+
init() {
18+
startFetching()
19+
}
20+
21+
func startFetching() {
22+
timer = Timer.publish(every: 1, on: .main, in: .common)
23+
.autoconnect()
24+
.sink { _ in self.fetchUsage() }
25+
}
26+
27+
func fetchUsage() {
28+
guard let url = URL(string: "http://127.0.0.1:8080/resource-usage") else { return }
29+
URLSession.shared.dataTask(with: url) { data, _, error in
30+
if let error = error {
31+
print("Error fetching usage:", error.localizedDescription)
32+
return
33+
}
34+
guard let data = data else { return }
35+
do {
36+
let usage = try JSONDecoder().decode(ResourceUsage.self, from: data)
37+
DispatchQueue.main.async {
38+
self.cpuUsage = usage.cpu_usage
39+
self.memoryUsage = usage.memory_usage
40+
self.diskUsage = usage.disk_usage
41+
}
42+
} catch {
43+
print("Decoding error:", error.localizedDescription)
44+
}
45+
}.resume()
46+
}
47+
48+
func toggleAlerts() {
49+
guard let url = URL(string: "http://127.0.0.1:8080/toggle-alerts") else { return }
50+
let body = ["enable_alerts": alertsEnabled]
51+
sendPostRequest(url: url, body: body)
52+
}
53+
54+
func changeLimits() {
55+
guard let url = URL(string: "http://127.0.0.1:8080/limit-changer"),
56+
let cpu = Double(cpuThreshold),
57+
let mem = Double(memoryThreshold),
58+
let disk = Double(diskThreshold)
59+
else {
60+
print("Invalid threshold input")
61+
return
62+
}
63+
let body: [String: Any] = [
64+
"cpu_threshold": cpu,
65+
"memory_threshold": mem,
66+
"disk_threshold": disk
67+
]
68+
sendPostRequest(url: url, body: body)
69+
}
70+
71+
func shutdownServer(completion: @escaping () -> Void) {
72+
guard let url = URL(string: "http://127.0.0.1:8080/shutdown") else { return }
73+
var request = URLRequest(url: url)
74+
request.httpMethod = "POST"
75+
URLSession.shared.dataTask(with: request) { _, _, error in
76+
if let error = error {
77+
print("Error shutting down:", error.localizedDescription)
78+
}
79+
DispatchQueue.main.async {
80+
completion()
81+
}
82+
}.resume()
83+
}
84+
85+
private func sendPostRequest(url: URL, body: [String: Any]) {
86+
var request = URLRequest(url: url)
87+
request.httpMethod = "POST"
88+
do {
89+
request.httpBody = try JSONSerialization.data(withJSONObject: body)
90+
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
91+
} catch {
92+
print("Error serializing JSON:", error.localizedDescription)
93+
return
94+
}
95+
URLSession.shared.dataTask(with: request) { _, _, error in
96+
if let error = error {
97+
print("POST request error:", error.localizedDescription)
98+
}
99+
}.resume()
100+
}
101+
}
102+
103+
struct ResourceUsage: Codable {
104+
let cpu_usage: Double
105+
let memory_usage: Double
106+
let disk_usage: Double
107+
}
108+
109+
struct ContentView: View {
110+
@StateObject private var vm = ResourceMonitorViewModel()
111+
@State private var isShuttingDown = false
112+
113+
var body: some View {
114+
VStack(alignment: .leading, spacing: 18) {
115+
Group {
116+
UsageBar(title: "CPU Usage", value: vm.cpuUsage, color: .red)
117+
UsageBar(title: "Memory Usage", value: vm.memoryUsage, color: .blue)
118+
UsageBar(title: "Disk Usage", value: vm.diskUsage, color: .purple)
119+
}
120+
121+
Divider()
122+
.padding(.vertical, 10)
123+
124+
Toggle("Enable Alerts", isOn: $vm.alertsEnabled)
125+
.onChange(of: vm.alertsEnabled) { _ in vm.toggleAlerts() }
126+
.toggleStyle(SwitchToggleStyle(tint: .pink))
127+
.padding(.bottom, 10)
128+
129+
Text("Change Resource Limits")
130+
.font(.title3).bold()
131+
.foregroundColor(.primary)
132+
133+
HStack(spacing: 12) {
134+
LimitInputField(text: $vm.cpuThreshold, placeholder: "CPU %")
135+
LimitInputField(text: $vm.memoryThreshold, placeholder: "Memory %")
136+
LimitInputField(text: $vm.diskThreshold, placeholder: "Disk %")
137+
138+
Button(action: vm.changeLimits) {
139+
Text("Apply")
140+
.bold()
141+
.padding(.vertical, 8)
142+
.padding(.horizontal, 16)
143+
.background(Color.accentColor)
144+
.foregroundColor(.white)
145+
.cornerRadius(8)
146+
.shadow(radius: 2)
147+
}
148+
.buttonStyle(PlainButtonStyle())
149+
.keyboardShortcut(.defaultAction)
150+
}
151+
152+
Divider()
153+
.padding(.vertical, 10)
154+
155+
Button(action: {
156+
isShuttingDown = true
157+
vm.shutdownServer {
158+
isShuttingDown = false
159+
NSApplication.shared.terminate(nil)
160+
}
161+
}) {
162+
Text(isShuttingDown ? "Shutting down..." : "Shutdown Server")
163+
.bold()
164+
.frame(maxWidth: .infinity)
165+
.padding()
166+
.background(isShuttingDown ? Color.gray : Color.red)
167+
.foregroundColor(.white)
168+
.cornerRadius(12)
169+
.shadow(radius: 3)
170+
}
171+
.disabled(isShuttingDown)
172+
173+
Spacer()
174+
}
175+
.padding(24)
176+
.frame(width: 400) // Wider app content width
177+
.background(
178+
RoundedRectangle(cornerRadius: 20)
179+
.fill(Color(.windowBackgroundColor))
180+
.shadow(color: .black.opacity(0.15), radius: 12, x: 0, y: 6)
181+
)
182+
.padding()
183+
}
184+
}
185+
186+
struct UsageBar: View {
187+
let title: String
188+
let value: Double
189+
let color: Color
190+
191+
var body: some View {
192+
VStack(alignment: .leading, spacing: 6) {
193+
Text(title)
194+
.font(.headline)
195+
.foregroundColor(color)
196+
ZStack(alignment: .leading) {
197+
RoundedRectangle(cornerRadius: 6)
198+
.frame(height: 14)
199+
.foregroundColor(Color.gray.opacity(0.3))
200+
RoundedRectangle(cornerRadius: 6)
201+
.frame(width: CGFloat(value / 100) * 360, height: 14) // adapt bar width
202+
.foregroundColor(color)
203+
.animation(.easeInOut(duration: 0.5), value: value)
204+
}
205+
Text(String(format: "%.1f%%", value))
206+
.font(.caption)
207+
.foregroundColor(.secondary)
208+
.padding(.leading, 4)
209+
}
210+
}
211+
}
212+
213+
struct LimitInputField: View {
214+
@Binding var text: String
215+
let placeholder: String
216+
217+
var body: some View {
218+
TextField(placeholder, text: $text)
219+
.textFieldStyle(RoundedBorderTextFieldStyle())
220+
.frame(width: 80)
221+
.multilineTextAlignment(.center)
222+
.font(.system(size: 14, weight: .semibold))
223+
}
224+
}
225+
226+
@main
227+
struct SysGuardApp: App {
228+
var body: some Scene {
229+
WindowGroup("SysGuard") {
230+
ContentView()
231+
}
232+
233+
.windowResizability(.contentSize) // window resizes to content size tightly
234+
}
235+
}

backend.log

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2025/07/13 19:32:49 Default limits: {90 90 90}
2+
2025/07/13 19:32:49 Starting backend server on port 8080...
3+
2025/07/13 19:33:01 Alerts enabled: false
4+
2025/07/13 19:33:02 Alerts enabled: true
5+
2025/07/13 19:33:03 Alerts enabled: false
6+
2025/07/13 19:33:15 Received shutdown request
7+
2025/07/13 19:33:15 Shutting down the server...
8+
2025/07/13 19:33:15 Server stopped.

backend/.DS_Store

6 KB
Binary file not shown.

backend/sysguard

7.55 MB
Binary file not shown.

frontend/image

-28.3 MB
Binary file not shown.

0 commit comments

Comments
 (0)