LogViewModel.swift 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. //
  2. // LogViewModel.swift
  3. // LoopFollow
  4. //
  5. // Created by Jonas Björkert on 2025-01-13.
  6. // Copyright © 2025 Jon Fawcett. All rights reserved.
  7. //
  8. import Foundation
  9. import Combine
  10. class LogViewModel: ObservableObject {
  11. @Published var allLogEntries: [LogEntry] = []
  12. @Published var filteredLogEntries: [LogEntry] = []
  13. @Published var selectedCategory: LogManager.Category? = nil
  14. @Published var searchText: String = ""
  15. private var cancellables = Set<AnyCancellable>()
  16. init() {
  17. Publishers.CombineLatest($selectedCategory, $searchText)
  18. .sink { [weak self] category, search in
  19. self?.filterLogs(category: category, searchText: search)
  20. }
  21. .store(in: &cancellables)
  22. loadLogEntries()
  23. // Optional: Set up a timer to refresh logs periodically
  24. Timer.publish(every: 5.0, on: .main, in: .common)
  25. .autoconnect()
  26. .sink { [weak self] _ in
  27. self?.loadLogEntries()
  28. }
  29. .store(in: &cancellables)
  30. }
  31. func loadLogEntries() {
  32. DispatchQueue.global(qos: .background).async { [weak self] in
  33. guard let self = self else { return }
  34. let logManager = LogManager.shared
  35. let logFileURL = logManager.currentLogFileURL
  36. guard FileManager.default.fileExists(atPath: logFileURL.path) else {
  37. DispatchQueue.main.async {
  38. self.allLogEntries = []
  39. self.filteredLogEntries = []
  40. }
  41. return
  42. }
  43. do {
  44. let logContent = try String(contentsOf: logFileURL, encoding: .utf8)
  45. var logLines = logContent.components(separatedBy: .newlines)
  46. logLines = logLines.filter { !$0.isEmpty }
  47. // Reverse the log lines to have newest first
  48. logLines.reverse()
  49. let uniqueLogEntries = logLines.map { LogEntry(id: UUID(), text: $0) }
  50. DispatchQueue.main.async {
  51. self.allLogEntries = uniqueLogEntries
  52. self.filterLogs(category: self.selectedCategory, searchText: self.searchText)
  53. }
  54. } catch {
  55. print("Error reading log file: \(error)")
  56. DispatchQueue.main.async {
  57. self.allLogEntries = []
  58. self.filteredLogEntries = []
  59. }
  60. }
  61. }
  62. }
  63. private func filterLogs(category: LogManager.Category?, searchText: String) {
  64. DispatchQueue.global(qos: .userInitiated).async { [weak self] in
  65. guard let self = self else { return }
  66. var filtered = self.allLogEntries
  67. // Filter by category
  68. if let category = category {
  69. let categoryTag = "[\(category.rawValue)]"
  70. filtered = filtered.filter { $0.text.contains(categoryTag) }
  71. }
  72. // Filter by search text
  73. if !searchText.isEmpty {
  74. filtered = filtered.filter { $0.text.localizedCaseInsensitiveContains(searchText) }
  75. }
  76. DispatchQueue.main.async {
  77. self.filteredLogEntries = filtered
  78. }
  79. }
  80. }
  81. }