Files
MacMonitor/Sources/App/SystemTelemetryStore.swift

84 lines
2.7 KiB
Swift

import Foundation
import SwiftUI
import Observation
@Observable
@MainActor
public final class SystemTelemetryStore {
public static let shared = SystemTelemetryStore()
public private(set) var isRunning: Bool = false
public private(set) var lastUpdateTimestamp: Date = .now
public private(set) var sampleInterval: TimeInterval = 1.0
/// Raw snapshots organized by provider identifier
public private(set) var latestSnapshot: [String: [String: Any]] = [:]
/// System identification
public let hostModel: String
public let osVersion: String
public let kernelVersion: String
public let physicalCpuCount: Int
public let logicalCpuCount: Int
private let coordinator = MMTelemetryCoordinator.shared
private init() {
self.hostModel = SystemTelemetryStore.readSysctlString("hw.model") ?? "Intel Mac"
self.osVersion = ProcessInfo.processInfo.operatingSystemVersionString
self.kernelVersion = SystemTelemetryStore.readSysctlString("kern.osrelease") ?? "Unknown Kernel"
self.physicalCpuCount = SystemTelemetryStore.readSysctlInt("hw.physicalcpu") ?? 1
self.logicalCpuCount = SystemTelemetryStore.readSysctlInt("hw.logicalcpu") ?? 1
setupCoordinator()
}
private func setupCoordinator() {
coordinator.sampleInterval = self.sampleInterval
coordinator.snapshotHandler = { [weak self] snapshot in
guard let self else { return }
self.latestSnapshot = snapshot
self.lastUpdateTimestamp = Date()
}
}
public func start() {
coordinator.start()
isRunning = coordinator.isRunning
}
public func stop() {
coordinator.stop()
isRunning = coordinator.isRunning
}
public func setSampleInterval(_ interval: TimeInterval) {
self.sampleInterval = interval
coordinator.sampleInterval = interval
}
public func triggerManualSample() {
coordinator.sampleImmediately()
}
public var registeredProviderCount: Int {
coordinator.registeredProviders().count
}
private static func readSysctlString(_ name: String) -> String? {
var size: size_t = 0
sysctlbyname(name, nil, &size, nil, 0)
guard size > 0 else { return nil }
var buffer = [CChar](repeating: 0, count: size)
sysctlbyname(name, &buffer, &size, nil, 0)
return String(cString: buffer)
}
private static func readSysctlInt(_ name: String) -> Int? {
var value: Int32 = 0
var size = MemoryLayout<Int32>.size
let result = sysctlbyname(name, &value, &size, nil, 0)
return result == 0 ? Int(value) : nil
}
}