Files
orca/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellWarmUp.swift
T
537d764f58 perf(mobile): warm WebKit's processes on iOS before the first page opens (#39851)
* perf(mobile): warm WebKit's processes on iOS before the first page opens

Builds a blank, non-persistent WKWebView a second after Home has a paired
host, so the first page open starts against WebContent, GPU and Network
processes already running. A release before the delayed start now makes
the start a no-op.

Based on #32163 by Jinwoo-H.

Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com>

* fix(mobile): guard warmUp on older binaries and release the warm view on page failure

Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com>

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com>
2026-11-08 05:01:36 -08:00

42 lines
1.7 KiB
Swift

import UIKit
import WebKit
/// The first page view in a process waits for WebKit to launch its helper processes. A blank view
/// loaded while Home is idle pays that instead. It is held until the first page has loaded, so that
/// page starts against processes already up, or until the app backgrounds, memory runs low or a
/// minute passes. It has its own non-persistent store and no bridge or scripts, so it shares no
/// state with a page.
enum MobileWebShellWarmUp {
private static var view: WKWebView?
private static var started = false
private static var observers: [NSObjectProtocol] = []
static func start() {
dispatchPrecondition(condition: .onQueue(.main))
// A launch into the background may never show a page, so it gets no web process.
guard !started, UIApplication.shared.applicationState == .active else { return }
started = true
let configuration = WKWebViewConfiguration()
configuration.websiteDataStore = .nonPersistent()
let view = WKWebView(frame: .zero, configuration: configuration)
view.loadHTMLString("", baseURL: nil)
self.view = view
DispatchQueue.main.asyncAfter(deadline: .now() + 60) { release() }
observers = [
UIApplication.didReceiveMemoryWarningNotification,
UIApplication.didEnterBackgroundNotification
].map { name in
NotificationCenter.default.addObserver(forName: name, object: nil, queue: .main) { _ in release() }
}
}
static func release() {
dispatchPrecondition(condition: .onQueue(.main))
// A page that finished before the delayed start already has its processes, so start must no-op.
started = true
view = nil
observers.forEach { NotificationCenter.default.removeObserver($0) }
observers = []
}
}