diff --git a/StikJIT/Utilities/LogManager.swift b/StikJIT/Utilities/LogManager.swift index 6b72983..91b2096 100644 --- a/StikJIT/Utilities/LogManager.swift +++ b/StikJIT/Utilities/LogManager.swift @@ -88,4 +88,16 @@ class LogManager: ObservableObject { self.errorCount = 0 } } + + func removeOldestLogs(count: Int) { + DispatchQueue.main.async { + // Remove the oldest logs and update error count + let removedLogs = self.logs.prefix(count) + self.logs.removeFirst(count) + + // Update error count by counting removed error logs + let removedErrorCount = removedLogs.filter { $0.type == .error }.count + self.errorCount = max(0, self.errorCount - removedErrorCount) + } + } } diff --git a/StikJIT/Views/ConsoleLogsView.swift b/StikJIT/Views/ConsoleLogsView.swift index 8e37aa6..9191ad2 100644 --- a/StikJIT/Views/ConsoleLogsView.swift +++ b/StikJIT/Views/ConsoleLogsView.swift @@ -23,6 +23,14 @@ struct ConsoleLogsView: View { @State private var alertTitle = "" @State private var isError = false + // Timer to check for log updates + @State private var logCheckTimer: Timer? = nil + + // Track if the view is active (visible) + @State private var isViewActive = false + @State private var lastProcessedLineCount = 0 // Track last processed line count + @State private var isLoadingLogs = false // Track loading state + private var accentColor: Color { if customAccentColorHex.isEmpty { return .blue @@ -74,6 +82,16 @@ struct ConsoleLogsView: View { } .onAppear { scrollView = proxy + isViewActive = true + // Load logs asynchronously + Task { + await loadIdeviceLogsAsync() + } + startLogCheckTimer() + } + .onDisappear { + isViewActive = false + stopLogCheckTimer() } .onChange(of: logManager.logs.count) { if autoScroll, let lastLog = logManager.logs.last { @@ -224,11 +242,23 @@ struct ConsoleLogsView: View { } ToolbarItem(placement: .navigationBarTrailing) { - Button(action: { - logManager.clearLogs() - }) { - Text("Clear") - .foregroundColor(accentColor) + HStack { + Button(action: { + // Load the logs again + Task { + await loadIdeviceLogsAsync() + } + }) { + Image(systemName: "arrow.clockwise") + .foregroundColor(accentColor) + } + + Button(action: { + logManager.clearLogs() + }) { + Text("Clear") + .foregroundColor(accentColor) + } } } } @@ -313,6 +343,144 @@ struct ConsoleLogsView: View { return .orange } } + + // Function to load idevice logs from file asynchronously + private func loadIdeviceLogsAsync() async { + guard !isLoadingLogs else { return } + isLoadingLogs = true + + // Get the path to the idevice log file + let logPath = URL.documentsDirectory.appendingPathComponent("idevice_log.txt").path + + // Check if the file exists + guard FileManager.default.fileExists(atPath: logPath) else { + await MainActor.run { + logManager.addInfoLog("No idevice logs found (Restart the app to continue reading)") + isLoadingLogs = false + } + return + } + + do { + // Read the file content + let logContent = try String(contentsOfFile: logPath, encoding: .utf8) + let lines = logContent.components(separatedBy: .newlines) + + // Only take the last 300 lines + let maxLines = 300 + let startIndex = max(0, lines.count - maxLines) + let recentLines = Array(lines[startIndex.. lastProcessedLineCount { + let newLines = Array(lines[lastProcessedLineCount.. maxLines { + let excessCount = logManager.logs.count - maxLines + logManager.removeOldestLogs(count: excessCount) + } + } + } + } catch { + await MainActor.run { + logManager.addErrorLog("Failed to read new logs: \(error.localizedDescription)") + } + } + + isLoadingLogs = false + } + + // Function to stop the timer + private func stopLogCheckTimer() { + logCheckTimer?.invalidate() + logCheckTimer = nil + } } struct ConsoleLogsView_Previews: PreviewProvider {