import Foundation final class PageIssueChecker: ObservableObject { @Published var isCheckingPages: Bool = false @Published var issues: Set = [] init() { } func check(pages: [Page], clearListBeforeStart: Bool = true) { guard !isCheckingPages else { return } isCheckingPages = true if clearListBeforeStart { issues = [] } DispatchQueue.global(qos: .userInitiated).async { for language in ContentLanguage.allCases { self.check(pages: pages, in: language) } DispatchQueue.main.async { self.isCheckingPages = false } } } private func check(pages: [Page], in language: ContentLanguage) { for page in pages { analyze(page: page, in: language) } } func check(page: Page, in language: ContentLanguage) { guard !isCheckingPages else { return } isCheckingPages = true DispatchQueue.global(qos: .userInitiated).async { self.analyze(page: page, in: language) DispatchQueue.main.async { self.isCheckingPages = false } } } private func analyze(page: Page, in language: ContentLanguage) { let results = page.content.results.makeResults(for: page, in: language) let parser = PageContentParser(content: page.content, language: language, results: results) let hasPreviousIssues = issues.contains { $0.page == page && $0.language == language } let pageIssues: [PageIssue] if let rawPageContent = page.content.storage.pageContent(for: page.id, language: language) { _ = parser.generatePage(from: rawPageContent) pageIssues = [] } else { let message = GenerationAnomaly.failedToLoadContent let error = PageIssue(page: page, language: language, message: message) pageIssues = [error] } guard hasPreviousIssues || !pageIssues.isEmpty else { return } update(issues: pageIssues, for: page, in: language) } private func update(issues: [PageIssue], for page: Page, in language: ContentLanguage) { let newIssues = self.issues .filter { $0.page != page || $0.language != language } .union(issues) DispatchQueue.main.async { self.issues = newIssues } } }