Files
Pulse/Sources/PulseUI/Features/Console/ConsoleViewModel.swift
2022-08-12 19:19:39 -04:00

118 lines
3.7 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// The MIT License (MIT)
//
// Copyright (c) 20202022 Alexander Grebenyuk (github.com/kean).
import CoreData
import Pulse
import Combine
import SwiftUI
final class ConsoleViewModel: NSObject, NSFetchedResultsControllerDelegate, ObservableObject {
#if os(iOS) || os(macOS)
let table: ConsoleTableViewModel
#endif
@Published private(set) var entities: [LoggerMessageEntity] = []
let details: ConsoleDetailsRouterViewModel
// Search criteria
let searchCriteria: ConsoleSearchCriteriaViewModel
@Published var isOnlyErrors: Bool = false
@Published var isOnlyNetwork: Bool = false
@Published var filterTerm: String = ""
var onDismiss: (() -> Void)?
private(set) var store: LoggerStore
private let controller: NSFetchedResultsController<LoggerMessageEntity>
private var isActive = false
private var latestSessionId: UUID?
private var cancellables: [AnyCancellable] = []
init(store: LoggerStore) {
self.store = store
self.details = ConsoleDetailsRouterViewModel()
let request = NSFetchRequest<LoggerMessageEntity>(entityName: "\(LoggerMessageEntity.self)")
request.fetchBatchSize = 100
request.relationshipKeyPathsForPrefetching = ["request"]
request.sortDescriptors = [NSSortDescriptor(keyPath: \LoggerMessageEntity.createdAt, ascending: false)]
self.controller = NSFetchedResultsController<LoggerMessageEntity>(fetchRequest: request, managedObjectContext: store.viewContext, sectionNameKeyPath: nil, cacheName: nil)
self.searchCriteria = ConsoleSearchCriteriaViewModel(store: store)
#if os(iOS) || os(macOS)
self.table = ConsoleTableViewModel(searchCriteriaViewModel: searchCriteria)
#endif
super.init()
controller.delegate = self
$filterTerm.throttle(for: 0.25, scheduler: RunLoop.main, latest: true).dropFirst().sink { [weak self] filterTerm in
self?.refresh(filterTerm: filterTerm)
}.store(in: &cancellables)
searchCriteria.dataNeedsReload.throttle(for: 0.5, scheduler: DispatchQueue.main, latest: true).sink { [weak self] in
self?.refreshNow()
}.store(in: &cancellables)
$isOnlyErrors.receive(on: DispatchQueue.main).dropFirst().sink { [weak self] _ in
self?.refreshNow()
}.store(in: &cancellables)
refreshNow()
}
// MARK: Appearance
func onAppear() {
isActive = true
reloadMessages()
}
func onDisappear() {
isActive = false
}
// MARK: Refresh
private func refreshNow() {
refresh(filterTerm: filterTerm)
}
private func refresh(filterTerm: String) {
// Get sessionId
if latestSessionId == nil {
latestSessionId = entities.first?.session
}
let sessionId = store === LoggerStore.shared ? LoggerStore.Session.current.id : latestSessionId
// Search messages
ConsoleSearchCriteria.update(request: controller.fetchRequest, filterTerm: filterTerm, criteria: searchCriteria.criteria, filters: searchCriteria.filters, sessionId: sessionId, isOnlyErrors: isOnlyErrors, isOnlyNetwork: isOnlyNetwork)
try? controller.performFetch()
reloadMessages()
}
// MARK: - NSFetchedResultsControllerDelegate
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith diff: CollectionDifference<NSManagedObjectID>) {
guard isActive else { return }
#if os(iOS) || os(macOS)
self.table.diff = diff
#endif
withAnimation {
reloadMessages()
}
}
private func reloadMessages() {
entities = controller.fetchedObjects ?? []
#if os(iOS) || os(macOS)
table.entities = entities
#endif
}
}