// // ContentRuleManager.swift // Prisme // // Author: Simon-Pierre Boucher // import Foundation import Observation import WebKit /// Compiles the bundled blocker rules into a `WKContentRuleList`. /// Compilation is async and off the rendering critical path: the first page /// can start loading before rules are ready; the pool applies them to every /// live web view as soon as compilation finishes. @MainActor @Observable final class ContentRuleManager { private static let identifier = "prisme-blocker-v1" private(set) var ruleList: WKContentRuleList? func compileIfNeeded() async { guard ruleList == nil else { return } guard let store = WKContentRuleListStore.default() else { return } // Reuse a previously compiled list when the bundle hasn't changed. if let cached = try? await store.contentRuleList(forIdentifier: Self.identifier) { ruleList = cached return } guard let url = Bundle.main.url(forResource: "blockerRules", withExtension: "json"), let json = try? String(contentsOf: url, encoding: .utf8) else { assertionFailure("blockerRules.json missing from bundle") return } do { ruleList = try await store.compileContentRuleList( forIdentifier: Self.identifier, encodedContentRuleList: json ) } catch { // Blocking is an enhancement, never a gate: browse without it. ruleList = nil } } }