From 678a71def7d39a8f02361918ba20d3f28e15c192 Mon Sep 17 00:00:00 2001 From: Yusuf Suleman Date: Fri, 3 Apr 2026 19:57:52 -0500 Subject: [PATCH] fix: pre-warm WebKit engine when Reader tab loads First WKWebView creation in a session is slow (~2-3s) because iOS lazily initializes the WebKit rendering engine. WebKitWarmer creates a hidden 1x1 WKWebView with empty HTML on Reader tab load, forcing the engine to initialize. Subsequent article opens are instant. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Reader/Views/ArticleWebView.swift | 30 ++++++++++++++++++- .../Features/Reader/Views/ReaderTabView.swift | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/ios/Platform/Platform/Features/Reader/Views/ArticleWebView.swift b/ios/Platform/Platform/Features/Reader/Views/ArticleWebView.swift index 9dbcf1f..74f2e98 100644 --- a/ios/Platform/Platform/Features/Reader/Views/ArticleWebView.swift +++ b/ios/Platform/Platform/Features/Reader/Views/ArticleWebView.swift @@ -1,6 +1,35 @@ import SwiftUI import WebKit +// MARK: - WebKit Pre-warmer + +/// Pre-warms the WebKit rendering engine on first use. +/// Call `WebKitWarmer.shared.warmUp()` early (e.g. when Reader tab loads) +/// so the first article open is instant. +final class WebKitWarmer { + static let shared = WebKitWarmer() + + private var warmedUp = false + private var warmView: WKWebView? + + func warmUp() { + guard !warmedUp else { return } + warmedUp = true + DispatchQueue.main.async { + let config = WKWebViewConfiguration() + let wv = WKWebView(frame: CGRect(x: 0, y: 0, width: 1, height: 1), configuration: config) + wv.loadHTMLString("", baseURL: nil) + // Hold reference briefly to ensure WebKit initializes + self.warmView = wv + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + self.warmView = nil + } + } + } +} + +// MARK: - Article Web View + struct ArticleWebView: UIViewRepresentable { let html: String @Binding var contentHeight: CGFloat @@ -49,7 +78,6 @@ struct ArticleWebView: UIViewRepresentable { } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - // Measure content height after render DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { webView.evaluateJavaScript("document.body.scrollHeight") { [weak self] result, _ in if let height = result as? CGFloat, height > 0 { diff --git a/ios/Platform/Platform/Features/Reader/Views/ReaderTabView.swift b/ios/Platform/Platform/Features/Reader/Views/ReaderTabView.swift index 84883d9..7fd54fb 100644 --- a/ios/Platform/Platform/Features/Reader/Views/ReaderTabView.swift +++ b/ios/Platform/Platform/Features/Reader/Views/ReaderTabView.swift @@ -138,6 +138,7 @@ struct ReaderTabView: View { } } .task { + WebKitWarmer.shared.warmUp() await vm.loadInitial() } }