| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- // LoopFollow
- // LogViewModel.swift
- import Combine
- import Foundation
- class LogViewModel: ObservableObject {
- @Published var allLogEntries: [LogEntry] = []
- @Published var filteredLogEntries: [LogEntry] = []
- @Published var selectedCategory: LogManager.Category? = nil
- @Published var searchText: String = ""
- private var cancellables = Set<AnyCancellable>()
- init() {
- Publishers.CombineLatest($selectedCategory, $searchText)
- .sink { [weak self] category, search in
- self?.filterLogs(category: category, searchText: search)
- }
- .store(in: &cancellables)
- loadLogEntries()
- Timer.publish(every: 5.0, on: .main, in: .common)
- .autoconnect()
- .sink { [weak self] _ in
- self?.loadLogEntries()
- }
- .store(in: &cancellables)
- }
- func loadLogEntries() {
- DispatchQueue.global(qos: .background).async { [weak self] in
- guard let self = self else { return }
- let logManager = LogManager.shared
- let logFileURL = logManager.currentLogFileURL
- guard FileManager.default.fileExists(atPath: logFileURL.path) else {
- DispatchQueue.main.async {
- self.allLogEntries = []
- self.filteredLogEntries = []
- }
- return
- }
- do {
- let logContent = try String(contentsOf: logFileURL, encoding: .utf8)
- var logLines = logContent.components(separatedBy: .newlines)
- logLines = logLines.filter { !$0.isEmpty }
- // Reverse the log lines to have newest first
- logLines.reverse()
- let uniqueLogEntries = logLines.map { LogEntry(id: UUID(), text: $0) }
- DispatchQueue.main.async {
- self.allLogEntries = uniqueLogEntries
- self.filterLogs(category: self.selectedCategory, searchText: self.searchText)
- }
- } catch {
- print("Error reading log file: \(error)")
- DispatchQueue.main.async {
- self.allLogEntries = []
- self.filteredLogEntries = []
- }
- }
- }
- }
- private func filterLogs(category: LogManager.Category?, searchText: String) {
- DispatchQueue.global(qos: .userInitiated).async { [weak self] in
- guard let self = self else { return }
- var filtered = self.allLogEntries
- // Filter by category and remove category tag
- if let category = category {
- let categoryTag = "[\(category.rawValue)] "
- filtered = filtered.filter { $0.text.contains(categoryTag) }
- .map { logEntry in
- var text = logEntry.text
- if let range = text.range(of: categoryTag) {
- text.removeSubrange(range)
- }
- return LogEntry(id: logEntry.id, text: text.trimmingCharacters(in: .whitespaces))
- }
- }
- // Filter by search text
- if !searchText.isEmpty {
- filtered = filtered.filter { $0.text.localizedCaseInsensitiveContains(searchText) }
- }
- DispatchQueue.main.async {
- self.filteredLogEntries = filtered
- }
- }
- }
- }
|