import AppKit import SwiftUI import WebKit struct RedditWebView: NSViewRepresentable { let url: URL let reloadTrigger: Int func makeCoordinator() -> Coordinator { Coordinator() } func makeNSView(context: Context) -> RedditWebViewContainer { let container = RedditWebViewContainer(webView: RedditWebViewManager.makeWebView()) container.webView.navigationDelegate = context.coordinator container.webView.uiDelegate = context.coordinator context.coordinator.container = container context.coordinator.lastReloadTrigger = reloadTrigger container.load(url: url) return container } func updateNSView(_ container: RedditWebViewContainer, context: Context) { guard context.coordinator.lastReloadTrigger != reloadTrigger else { return } context.coordinator.lastReloadTrigger = reloadTrigger container.load(url: url) } final class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate { weak var container: RedditWebViewContainer? var lastReloadTrigger = -1 func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { webView.evaluateJavaScript(RedditPostBoundaryStyle.injectionScript, completionHandler: nil) } func webViewWebContentProcessDidTerminate(_ webView: WKWebView) { container?.reloadLastURL() } func webView( _ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error ) { let nsError = error as NSError guard nsError.domain != NSURLErrorDomain || nsError.code != NSURLErrorCancelled else { return } container?.reloadLastURL(after: 1.0) } func webView( _ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void ) { guard let url = navigationAction.request.url else { decisionHandler(.allow) return } if navigationAction.targetFrame == nil { webView.load(URLRequest(url: url)) decisionHandler(.cancel) return } if navigationAction.navigationType == .linkActivated, !Self.isRedditURL(url) { NSWorkspace.shared.open(url) decisionHandler(.cancel) return } decisionHandler(.allow) } func webView( _ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures ) -> WKWebView? { if let url = navigationAction.request.url { webView.load(URLRequest(url: url)) } return nil } private static func isRedditURL(_ url: URL) -> Bool { guard let host = url.host?.lowercased() else { return false } let redditHosts = [ "reddit.com", "www.reddit.com", "old.reddit.com", "new.reddit.com", "reddit.app.link", "redd.it", ] return redditHosts.contains(where: { host == $0 || host.hasSuffix(".\($0)") }) } } } final class RedditWebViewContainer: NSView { let webView: WKWebView private var pendingURL: URL? private var lastLoadedURL: URL? private var reloadWorkItem: DispatchWorkItem? init(webView: WKWebView) { self.webView = webView super.init(frame: .zero) wantsLayer = true addSubview(webView) webView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ webView.leadingAnchor.constraint(equalTo: leadingAnchor), webView.trailingAnchor.constraint(equalTo: trailingAnchor), webView.topAnchor.constraint(equalTo: topAnchor), webView.bottomAnchor.constraint(equalTo: bottomAnchor), ]) } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func load(url: URL) { pendingURL = url lastLoadedURL = url reloadWorkItem?.cancel() tryLoadIfReady() } func reloadLastURL(after delay: TimeInterval = 0) { guard let lastLoadedURL else { return } reloadWorkItem?.cancel() let workItem = DispatchWorkItem { [weak self] in self?.load(url: lastLoadedURL) } reloadWorkItem = workItem DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: workItem) } override func layout() { super.layout() tryLoadIfReady() } override func viewDidMoveToWindow() { super.viewDidMoveToWindow() tryLoadIfReady() } private func tryLoadIfReady() { guard let url = pendingURL else { return } guard bounds.width > 1, bounds.height > 1 else { return } pendingURL = nil webView.load(URLRequest(url: url)) } }