fix: pre-warm WebKit engine when Reader tab loads
All checks were successful
Security Checks / dockerfile-lint (push) Successful in 3s
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s

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) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-03 19:57:52 -05:00
parent 3f16ca44be
commit 678a71def7
2 changed files with 30 additions and 1 deletions

View File

@@ -1,6 +1,35 @@
import SwiftUI import SwiftUI
import WebKit 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("<html><body></body></html>", 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 { struct ArticleWebView: UIViewRepresentable {
let html: String let html: String
@Binding var contentHeight: CGFloat @Binding var contentHeight: CGFloat
@@ -49,7 +78,6 @@ struct ArticleWebView: UIViewRepresentable {
} }
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// Measure content height after render
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
webView.evaluateJavaScript("document.body.scrollHeight") { [weak self] result, _ in webView.evaluateJavaScript("document.body.scrollHeight") { [weak self] result, _ in
if let height = result as? CGFloat, height > 0 { if let height = result as? CGFloat, height > 0 {

View File

@@ -138,6 +138,7 @@ struct ReaderTabView: View {
} }
} }
.task { .task {
WebKitWarmer.shared.warmUp()
await vm.loadInitial() await vm.loadInitial()
} }
} }